diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..c20344600 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +temp/ +build/ +*.md \ No newline at end of file diff --git a/codecov.yml b/.github/codecov.yml similarity index 65% rename from codecov.yml rename to .github/codecov.yml index 45b3a6429..dcb3fa110 100644 --- a/codecov.yml +++ b/.github/codecov.yml @@ -14,14 +14,21 @@ parsers: method: no conditional: yes coverage: - range: 40..100 + range: 40..92 round: down precision: 2 + # status: See https://docs.codecov.com/docs/commit-status status: project: default: + target: 60% if_not_found: success - if_ci_failed: error + threshold: 1% # Allow coverage to drop by X%, posting a success status. + # removed_code_behavior: Takes values [off, removals_only, adjust_base] + removed_code_behavior: adjust_base + patch: + default: + target: 0% comment: # this is a top-level key layout: " diff, flags, files" diff --git a/.github/issue-labeler-config.yml b/.github/issue-labeler-config.yml new file mode 100644 index 000000000..1d7046b6a --- /dev/null +++ b/.github/issue-labeler-config.yml @@ -0,0 +1,3 @@ +# Adds the "S-triage" label ot any issue that gets opened. +S-triage: + - '/.*/' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ae4278186..e7349fe6b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,8 @@ -# Description +# Purpose / Abstract -What does this PR do? - -# Purpose +- Closes #AAA -Why is this PR important? + diff --git a/.github/workflows/changelog-deps.yml b/.github/workflows/changelog-deps.yml index b9336196d..4a3c574c1 100644 --- a/.github/workflows/changelog-deps.yml +++ b/.github/workflows/changelog-deps.yml @@ -1,17 +1,21 @@ name: "Automatically update changelog with dependabot" -on: +on: pull_request: types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] jobs: changelog-update: runs-on: ubuntu-latest + if: contains(github.event.pull_request.labels.*.name, 'dependabot') - # TODO: feat: try to use author of the commit(s) to see if it's dependabot - # ${{ any(contains(commit.author.username, 'dependabot') for commit in github.event.commits) }} - steps: + # TODO: feat: try to use author of the commit(s) to see if it's dependabot + # instead of using labels. Labels work fine for now. + # ${{ any(contains(commit.author.username, 'dependabot') for commit in github.event.commits) }} + + steps: - uses: actions/checkout@v4 with: + # Token with permissions for pushing commits and editting GH issues token: ${{ secrets.NIBIRU_PM }} # to avoid checking out the repo in a detached state ref: ${{ github.head_ref }} @@ -19,8 +23,8 @@ jobs: # This step updates adds a line to the "## Unreleased" section - uses: dangoslen/dependabot-changelog-helper@v3 with: - activationLabel: 'dependabot' - changelogPath: './CHANGELOG.md' + activationLabel: "dependabot" + changelogPath: "./CHANGELOG.md" - uses: stefanzweifel/git-auto-commit-action@v5 with: diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 000000000..c25a65dc7 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,52 @@ +name: Publish Docker Image + +on: + push: + branches: + - main + tags: + - v* + +jobs: + chaosnet: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Get version + id: get_version + uses: battila7/get-version-action@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR container register + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push tagged chaosnet image + uses: docker/build-push-action@v6 + if: startsWith(github.ref, 'refs/tags/v') + with: + file: contrib/docker/chaosnet.Dockerfile + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ghcr.io/nibiruchain/chaosnet:${{ steps.get_version.outputs.version }} + + - name: Build and push latest chaosnet image + uses: docker/build-push-action@v6 + if: startsWith(github.ref, 'refs/heads/main') + with: + file: contrib/docker/chaosnet.Dockerfile + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ghcr.io/nibiruchain/chaosnet:latest \ No newline at end of file diff --git a/.github/workflows/e2e-evm.yml b/.github/workflows/e2e-evm.yml new file mode 100644 index 000000000..8e304b0ae --- /dev/null +++ b/.github/workflows/e2e-evm.yml @@ -0,0 +1,84 @@ +name: EVM E2E tests + +on: + pull_request: + paths: + [ + "**.go", + "**.proto", + "go.mod", + "go.sum", + "**go.mod", + "**go.sum", + "contrib/docker/*", + "**.ts", + "**.js", + "**.json", + ] + +# Allow concurrent runs on main/release branches but isolates other branches +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }} + cancel-in-progress: ${{ ! (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} + +jobs: + e2e-evm: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.21 + cache: true + + # Use GitHub actions output paramters to get go paths. For more info, see + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions + - name: "Set output variables for go cache" + id: go-cache-paths + run: | + echo "go-build-cache=$(go env GOCACHE)" >> $GITHUB_OUTPUT + echo "go-mod-cache=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT + + - name: "Go build cache" + uses: actions/cache@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-build-cache }} + key: go-build-cache-${{ hashFiles('**/go.sum') }} + + - name: "Go mod cache" + uses: actions/cache@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-mod-cache }} + key: go-mod-cache-${{ hashFiles('**/go.sum') }} + + - name: "Install just" + # casey/just: https://just.systems/man/en/chapter_6.html + # taiki-e/install-action: https://github.com/taiki-e/install-action + uses: taiki-e/install-action@just + + - name: "Build the nibid binary" + run: | + just install + + - name: Setup NodeJS with npm caching + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: "just install" + run: just install + working-directory: "evm-e2e" + + - name: "Launch localnet" + run: | + just localnet --no-build & + sleep 10 + + - name: "Run tests (just test)" + run: just test + working-directory: "evm-e2e" + env: + JSON_RPC_ENDPOINT: http://127.0.0.1:8545 + MNEMONIC: guard cream sadness conduct invite crumble clock pudding hole grit liar hotel maid produce squeeze return argue turtle know drive eight casino maze host diff --git a/.github/workflows/e2e-wasm.yml b/.github/workflows/e2e-wasm.yml index ac37a71b3..93e04146d 100644 --- a/.github/workflows/e2e-wasm.yml +++ b/.github/workflows/e2e-wasm.yml @@ -1,5 +1,4 @@ ---- -name: CosmWasm e2e tests +name: Wasm E2E tests on: # On normal PRs or when workflow goreleaser finishes, as it gets the last release tag. @@ -8,7 +7,7 @@ on: # Allow concurrent runs on main/release branches but isolates other branches concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }} - cancel-in-progress: ${{ ! (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }} + cancel-in-progress: ${{ ! (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} jobs: e2e-wasm: @@ -19,13 +18,13 @@ jobs: fetch-depth: 0 - name: Download release id: latest_release - uses: pozetroninc/github-action-get-latest-release@v0.7.0 + uses: pozetroninc/github-action-get-latest-release@v0.8.0 with: repository: ${{ github.repository }} token: ${{ secrets.GITHUB_TOKEN }} - name: download release - uses: robinraju/release-downloader@v1.8 + uses: robinraju/release-downloader@v1.11 with: # uses latest (including drafts) # tag: ${{ steps.latest_release.outputs.release }} diff --git a/.github/workflows/gh-issues.yml b/.github/workflows/gh-issues.yml new file mode 100644 index 000000000..69054648e --- /dev/null +++ b/.github/workflows/gh-issues.yml @@ -0,0 +1,38 @@ +name: "Auto-add GH issues to project" +# Add all issues opened to the issue board for triage and assignment +# GitHub Org and Project Automation +# https://www.notion.so/nibiru/GitHub-Org-and-Project-Automation-c771d671109849ee9fda7c8b741cd66a?pvs=4 + +on: + issues: + types: ["opened", "labeled"] + +permissions: + issues: write + contents: read + +jobs: + # https://github.com/actions/add-to-project + add-to-project: + name: "Add GH ticket to project" + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@v1.0.2 + with: + project-url: https://github.com/orgs/NibiruChain/projects/8 + github-token: ${{ secrets.NIBIRU_PM }} + + label-triage: + name: "Add GH ticket to project" + runs-on: ubuntu-latest + # The action comes from the "Activty types" for the "issues" webhook event + # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#issues + if: "github.event.action == 'opened'" + steps: + - uses: github/issue-labeler@v3.4 + if: join(github.event.issue.labels) == '' + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + configuration-path: ".github/issue-labeler-config.yml" + enable-versioned-regex: 0 + not-before: "2024-05-01T00:00:00Z" diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 6f2e55a76..58129361f 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -1,17 +1,17 @@ name: Linter -# Run on every master merge and on PRs. +# Run on every main merge and on PRs. on: push: - branches: ["master"] - paths: ["**.go", "**.proto", "go.mod", "go.sum"] + branches: ["main"] + paths: ["**.go", "**.proto", "go.mod", "go.sum", "**go.mod", "**go.sum"] pull_request: - paths: ["**.go", "**.proto", "go.mod", "go.sum"] + paths: ["**.go", "**.proto", "go.mod", "go.sum", "**go.mod", "**go.sum"] -# Allow concurrent runs on main/release branches but isolates other branches +# Allow concurrent runs on main/release branches but isolates other branches concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }} - cancel-in-progress: ${{ ! (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }} + cancel-in-progress: ${{ ! (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} permissions: contents: read @@ -23,13 +23,33 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: 1.21 cache: false # the golangci-lint action already caches for us (https://github.com/golangci/golangci-lint-action#performance) + # Use GitHub actions output paramters to get go paths. For more info, see + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions + - name: "Set output variables for go cache" + id: go-cache-paths + run: | + echo "go-build-cache=$(go env GOCACHE)" >> $GITHUB_OUTPUT + echo "go-mod-cache=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT + + - name: "Go build cache" + uses: actions/cache@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-build-cache }} + key: go-build-cache-${{ hashFiles('**/go.sum') }} + + - name: "Go mod cache" + uses: actions/cache@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-mod-cache }} + key: go-mod-cache-${{ hashFiles('**/go.sum') }} + - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: v1.54.2 diff --git a/.github/workflows/goreleaser-latest.yml b/.github/workflows/goreleaser-latest.yml new file mode 100644 index 000000000..d5db8df26 --- /dev/null +++ b/.github/workflows/goreleaser-latest.yml @@ -0,0 +1,47 @@ +name: goreleaser-latest + +on: + push: + branches: + - main + - develop + +permissions: + contents: write + packages: write + +jobs: + goreleaser-latest: + runs-on: ubuntu-latest-m + steps: + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - run: git fetch --force --tags + + - run: make release-snapshot + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Login to GHCR container register + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push versioned image + uses: docker/build-push-action@v6 + with: + file: contrib/docker/goreleaser.Dockerfile + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ghcr.io/nibiruchain/nibiru:latest diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index e77ba3ac8..dabf195d4 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -41,7 +41,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push versioned image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: file: contrib/docker/goreleaser.Dockerfile context: . diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8fcd9c59f..4183e3eff 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -4,14 +4,16 @@ on: workflow_dispatch: # Runs on manual calls schedule: - cron: "0 0 * * *" # Runs automatically every day + push: + branches: ["main"] pull_request: # paths makes the action run only when the given paths are changed - paths: ["**.go", "**.proto", "go.mod", "go.sum"] + paths: ["**.go", "**.proto", "go.mod", "go.sum", "**go.mod", "**go.sum"] # Allow concurrent runs on main/release branches but isolates other branches concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }} - cancel-in-progress: ${{ ! (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }} + cancel-in-progress: ${{ ! (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} jobs: integration-tests: @@ -22,13 +24,35 @@ jobs: - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: 1.21 cache: true - - name: Run all integration tests. + # Use GitHub actions output paramters to get go paths. For more info, see + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions + - name: "Set output variables for go cache" + id: go-cache-paths + run: | + echo "go-build-cache=$(go env GOCACHE)" >> $GITHUB_OUTPUT + echo "go-mod-cache=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT + + - name: "Go build cache" + uses: actions/cache@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-build-cache }} + key: go-build-cache-${{ hashFiles('**/go.sum') }} + + - name: "Go mod cache" + uses: actions/cache@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-mod-cache }} + key: go-mod-cache-${{ hashFiles('**/go.sum') }} + + - name: "Run all integration tests." run: make test-coverage-integration - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + - name: "Upload coverage reports to Codecov" + uses: codecov/codecov-action@v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/pr-title-lint.yml b/.github/workflows/pr-title-lint.yml index ca5c105e3..a3c6bedc8 100644 --- a/.github/workflows/pr-title-lint.yml +++ b/.github/workflows/pr-title-lint.yml @@ -8,9 +8,11 @@ jobs: pr-lint: runs-on: ubuntu-latest steps: - - uses: seferov/pr-lint-action@master + - uses: seferov/pr-lint-action@v1.2.0 with: # taken from https://gist.github.com/marcojahn/482410b728c31b221b70ea6d2c433f0c title-regex: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(\([\w\-\.]+\))?(!)?: ([\w ])+([\s\S]*)' - title-regex-flags: "g" # optional - error-message: "Please follow conventional commit style: https://www.conventionalcommits.org/en/v1.0.0/" # optional + # title-regex-flags (Optional) + title-regex-flags: "g" + # error-message (Optional) + error-message: "Please follow conventional commit style: https://www.conventionalcommits.org/en/v1.0.0/" diff --git a/.github/workflows/proto-lint.yml b/.github/workflows/proto-lint.yml index c1a7e7bf4..73328662a 100644 --- a/.github/workflows/proto-lint.yml +++ b/.github/workflows/proto-lint.yml @@ -9,7 +9,7 @@ on: # Allow concurrent runs on main/release branches but isolates other branches concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }} - cancel-in-progress: ${{ ! (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }} + cancel-in-progress: ${{ ! (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} permissions: contents: read @@ -22,7 +22,7 @@ jobs: # timeout-minutes: 5 # steps: # - uses: actions/checkout@v4 - # - uses: bufbuild/buf-setup-action@v1.27.1 + # - uses: bufbuild/buf-setup-action@v1.47.2 # - uses: bufbuild/buf-lint-action@v1 # with: # input: "proto" @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.27.1 + - uses: bufbuild/buf-setup-action@v1.47.2 with: github_token: ${{ github.token }} - uses: bufbuild/buf-breaking-action@v1 diff --git a/.github/workflows/sims.yml b/.github/workflows/sims.yml deleted file mode 100644 index afa2c6df0..000000000 --- a/.github/workflows/sims.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Run short simulations - -on: - pull_request: - paths: ["**.go", "**.proto", "go.mod", "go.sum"] - -jobs: - install-runsim: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 - with: - go-version: 1.19 - cache: true - - uses: actions/cache@v3 - with: - path: ~/go/bin - key: ${{ runner.os }}-go-runsim-binary - - name: Install runsim - run: go install github.com/cosmos/tools/cmd/runsim@v1.0.0 - - test-sim-nondeterminism: - runs-on: ubuntu-latest - needs: [install-runsim] - steps: - - uses: actions/checkout@v4 - - uses: technote-space/get-diff-action@v6 - with: - SUFFIX_FILTER: | - **/**.go - go.mod - go.sum - - uses: actions/setup-go@v4 - with: - go-version: 1.19 - cache: true - if: env.GIT_DIFF - - uses: actions/cache@v3 - with: - path: ~/go/bin - key: ${{ runner.os }}-go-runsim-binary - if: env.GIT_DIFF - - name: test-sim-nondeterminism - run: | - make test-sim-nondeterminism - if: env.GIT_DIFF diff --git a/.github/workflows/simulation-tests.yml b/.github/workflows/simulation-tests.yml new file mode 100644 index 000000000..fa5888834 --- /dev/null +++ b/.github/workflows/simulation-tests.yml @@ -0,0 +1,59 @@ +name: Simulation tests + +on: + push: + branches: + # every push to default branch + - main + schedule: + # once per day + - cron: "0 0 * * *" + +jobs: + test-sim-nondeterminism: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.21 + cache: true + - name: TestAppStateDeterminism + run: | + make test-sim-nondeterminism + + test-sim-default-genesis-fast: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.21 + cache: true + - name: TestFullAppSimulation + run: | + make test-sim-default-genesis-fast + + test-sim-import-export: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.21 + cache: true + - name: TestAppImportExport + run: | + make test-sim-import-export + + test-sim-after-import: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.21 + cache: true + - name: TestAppSimulationAfterImport + run: | + make test-sim-after-import \ No newline at end of file diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index c91bf48af..a639669ba 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -2,12 +2,12 @@ name: Unit Tests on: pull_request: - paths: ["**.go", "**.proto", "go.mod", "go.sum"] + paths: ["**.go", "**.proto", "go.mod", "go.sum", "**go.mod", "**go.sum", "contrib/docker/*"] -# Allow concurrent runs on main/release branches but isolates other branches +# Allow concurrent runs on main/release branches but isolates other branches concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }} - cancel-in-progress: ${{ ! (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }} + cancel-in-progress: ${{ ! (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) }} jobs: unit-tests: @@ -16,23 +16,57 @@ jobs: - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: 1.21 cache: true + # Use GitHub actions output paramters to get go paths. For more info, see + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions + - name: "Set output variables for go cache" + id: go-cache-paths + run: | + echo "go-build-cache=$(go env GOCACHE)" >> $GITHUB_OUTPUT + echo "go-mod-cache=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT + + - name: "Go build cache" + uses: actions/cache@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-build-cache }} + key: go-build-cache-${{ hashFiles('**/go.sum') }} + + - name: "Go mod cache" + uses: actions/cache@v4 + with: + path: ${{ steps.go-cache-paths.outputs.go-mod-cache }} + key: go-mod-cache-${{ hashFiles('**/go.sum') }} + - name: Run all unit tests. - run: make test-coverage + run: make test-unit build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + + - name: "Set up Go" + uses: actions/setup-go@v5 + with: + go-version: 1.21 + + - name: "Install just" + # casey/just: https://just.systems/man/en/chapter_6.html + # taiki-e/install-action: https://github.com/taiki-e/install-action + uses: taiki-e/install-action@just + + - name: "Build the nibid binary" + run: | + just install - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: 1.19 + - name: "Run scripts/chaosnet.sh (Used in Docker image)" + run: | + just test-chaosnet - - name: Build the nibid binary - run: make build + - name: "Run scripts/localnet.sh" + run: | + just test-localnet diff --git a/.gitignore b/.gitignore index 09a8df65f..1906a076b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -**__pycache** +### Main Gitignore unit-test-reports vue/node_modules vue/dist @@ -11,7 +11,352 @@ data build *_old mockdata/ -docs +docs/static dist coverage.txt +coverage.html temp +temp* +txout.json +vote.json +**__pycache** +scratch-paper.md +logs + +### TypeScript and Friends + +node_modules +ui-debug.log +.firebase/ +.idea +.vscode +/public/ +.env +firebase-debug.log +out-* +exit-status-* +.DS_Store +.npmrc + +### Node ### + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + + + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### react ### +.DS_* +*.log +logs +**/*.backup.* +**/*.back.* + +node_modules +bower_components + +*.sublime* + +psd +thumb +sketch + +.firebase/ + +public/js/ +.npmrc + +playwright/test-results/ +playwright/playwright-report/ +playwright/playwright/.cache/ +playwright/chrome-extensions/keplr/ +playwright/yarn.lock diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..b0ef69b2c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "geth"] + path = geth + url = git@github.com:NibiruChain/go-ethereum.git diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..deed13c01 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +lts/jod diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c2a3207f..0d6620941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,862 +38,336 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## [Unreleased] + +### Nibiru EVM - 2025-02-10 + +- [#2119](https://github.com/NibiruChain/nibiru/pull/2119) - fix(evm): Guarantee + that gas consumed during any send operation of the "NibiruBankKeeper" depends + only on the "bankkeeper.BaseKeeper"'s gas consumption. +- [#2120](https://github.com/NibiruChain/nibiru/pull/2120) - fix: Use canonical hexadecimal strings for Eip155 address encoding +- [#2122](https://github.com/NibiruChain/nibiru/pull/2122) - test(evm): more bank extension tests and EVM ABCI integration tests to prevent regressions +- [#2124](https://github.com/NibiruChain/nibiru/pull/2124) - refactor(evm): + Remove unnecessary argument in the `VerifyFee` function, which returns the token + payment required based on the effective fee from the tx data. Improve + documentation. +- [#2125](https://github.com/NibiruChain/nibiru/pull/2125) - feat(evm-precompile):Emit EVM events created to reflect the ABCI events that occur outside the EVM to make sure that block explorers and indexers can find indexed ABCI event information. +- [#2127](https://github.com/NibiruChain/nibiru/pull/2127) - fix(vesting): disabled built in auth/vesting module functionality +- [#2129](https://github.com/NibiruChain/nibiru/pull/2129) - fix(evm): issue with infinite recursion in erc20 funtoken contracts +- [#2130](https://github.com/NibiruChain/nibiru/pull/2130) - fix(evm): proper nonce management in statedb +- [#2132](https://github.com/NibiruChain/nibiru/pull/2132) - fix(evm): proper tx gas refund +- [#2134](https://github.com/NibiruChain/nibiru/pull/2134) - fix(evm): query of NIBI should use bank state, not the StateDB +- [#2139](https://github.com/NibiruChain/nibiru/pull/2139) - fix(evm): erc20 born funtoken: properly burn bank coins after converting coin back to erc20 +- [#2140](https://github.com/NibiruChain/nibiru/pull/2140) - fix(bank): bank keeper extension now charges gas for the bank operations +- [#2141](https://github.com/NibiruChain/nibiru/pull/2141) - refactor: simplify account retrieval operation in `nibid q evm account`. +- [#2142](https://github.com/NibiruChain/nibiru/pull/2142) - fix(bank): add additional missing methods to the NibiruBankKeeper +- [#2144](https://github.com/NibiruChain/nibiru/pull/2144) - feat(token-registry): Implement strongly typed Nibiru Token Registry and generation command +- [#2145](https://github.com/NibiruChain/nibiru/pull/2145) - chore(token-registry): add xNIBI Astrovault LST to registry +- [#2147](https://github.com/NibiruChain/nibiru/pull/2147) - fix(simapp): manually add x/vesting Cosmos-SDK module types to the codec in simulation tests since they are expected by default +- [#2149](https://github.com/NibiruChain/nibiru/pull/2149) - feat(evm-oracle): +add Solidity contract that we can use to expose the Nibiru Oracle in the +ChainLink interface. Publish all precompiled contracts and ABIs on npm under +the `@nibiruchain/solidity` package. +- [#2151](https://github.com/NibiruChain/nibiru/pull/2151) - feat(evm): randao support for evm +- [#2152](https://github.com/NibiruChain/nibiru/pull/2152) - fix(precompile): consume gas for precompile calls regardless of error +- [#2154](https://github.com/NibiruChain/nibiru/pull/2154) - fix(evm): +JSON encoding for the `EIP55Addr` struct was not following the Go conventions and +needed to include double quotes around the hexadecimal string. +- [#2156](https://github.com/NibiruChain/nibiru/pull/2156) - test(evm-e2e): add E2E test using the Nibiru Oracle's ChainLink impl +- [#2157](https://github.com/NibiruChain/nibiru/pull/2157) - fix(evm): Fix unit inconsistency related to AuthInfo.Fee and txData.Fee using effective fee +- [#2159](https://github.com/NibiruChain/nibiru/pull/2159) - chore(evm): Augment the Wasm msg handler so that wasm contracts cannot send MsgEthereumTx +- [#2160](https://github.com/NibiruChain/nibiru/pull/2160) - fix(evm-precompile): use bank.MsgServer Send in precompile IFunToken.bankMsgSend +- [#2161](https://github.com/NibiruChain/nibiru/pull/2161) - fix(evm): added tx logs events to the funtoken related txs +- [#2162](https://github.com/NibiruChain/nibiru/pull/2162) - test(testutil): try retrying for 'panic: pebbledb: closed' +- [#2167](https://github.com/NibiruChain/nibiru/pull/2167) - refactor(evm): removed blockGasUsed transient variable +- [#2168](https://github.com/NibiruChain/nibiru/pull/2168) - chore(evm-solidity): Move unrelated docs, gen-embeds, and add Solidity docs +- [#2165](https://github.com/NibiruChain/nibiru/pull/2165) - fix(evm): use Singleton StateDB pattern for EVM txs +- [#2169](https://github.com/NibiruChain/nibiru/pull/2169) - fix(evm): Better handling erc20 metadata +- [#2170](https://github.com/NibiruChain/nibiru/pull/2170) - chore: Remove redundant allowUnprotectedTxs +- [#2172](https://github.com/NibiruChain/nibiru/pull/2172) - chore: close iterator in IterateEpochInfo +- [#2173](https://github.com/NibiruChain/nibiru/pull/2173) - fix(evm): clear `StateDB` between calls +- [#2177](https://github.com/NibiruChain/nibiru/pull/2177) - fix(cmd): Continue from #2127 and unwire vesting flags and logic from genaccounts.go +- [#2176](https://github.com/NibiruChain/nibiru/pull/2176) - tests(evm): add dirty state tests from code4rena audit +- [#2180](https://github.com/NibiruChain/nibiru/pull/2180) - fix(evm): apply gas consumption across the entire EVM codebase at `CallContractWithInput` +- [#2183](https://github.com/NibiruChain/nibiru/pull/2183) - fix(evm): bank keeper extension gas meter type +- [#2184](https://github.com/NibiruChain/nibiru/pull/2184) - test(evm): e2e tests configuration enhancements +- [#2187](https://github.com/NibiruChain/nibiru/pull/2187) - fix(evm): fix eip55 address encoding +- [#2188](https://github.com/NibiruChain/nibiru/pull/2188) - refactor(evm): update logs emission +- [#2192](https://github.com/NibiruChain/nibiru/pull/2192) - fix(oracle): correctly handle misscount +- [#2104](https://github.com/NibiruChain/nibiru/pull/2074) - chore: update chain IDs + +#### Nibiru EVM | Before Audit 2 - 2024-12-06 + +The codebase went through a third-party [Code4rena +Zenith](https://code4rena.com/zenith) Audit, running from 2024-10-07 until +2024-11-01 and including both a primary review period and mitigation/remission +period. This section describes code changes that occurred after that audit in +preparation for a second audit starting in November 2024. + +- [#2074](https://github.com/NibiruChain/nibiru/pull/2074) - fix(evm-keeper): better utilize ERC20 metadata during FunToken creation. The bank metadata for a new FunToken mapping ties a connection between the Bank Coin's `DenomUnit` and the ERC20 contract metadata like the name, decimals, and symbol. This change brings parity between EVM wallets, such as MetaMask, and Interchain wallets like Keplr and Leap. +- [#2076](https://github.com/NibiruChain/nibiru/pull/2076) - fix(evm-gas-fees): + Use effective gas price in RefundGas and make sure that units are properly + reflected on all occurrences of "base fee" in the codebase. This fixes [#2059](https://github.com/NibiruChain/nibiru/issues/2059) + and the [related comments from @Unique-Divine and @berndartmueller](https://github.com/NibiruChain/nibiru/issues/2059#issuecomment-2408625724). +- [#2084](https://github.com/NibiruChain/nibiru/pull/2084) - feat(evm-forge): foundry support and template for Nibiru EVM development +- [#2086](https://github.com/NibiruChain/nibiru/pull/2086) - fix(evm-precomples): + Fix state consistency in precompile execution by ensuring proper journaling of + state changes in the StateDB. This pull request makes sure that state is + committed as expected, fixes the `StateDB.Commit` to follow its guidelines more + closely, and solves for a critical state inconsistency producible from the + FunToken.sol precompiled contract. It also aligns the precompiles to use + consistent setup and dynamic gas calculations, addressing the following tickets. + - + - + - +- [#2088](https://github.com/NibiruChain/nibiru/pull/2088) - refactor(evm): remove outdated comment and improper error message text +- [#2089](https://github.com/NibiruChain/nibiru/pull/2089) - better handling of gas consumption within erc20 contract execution +- [#2090](https://github.com/NibiruChain/nibiru/pull/2090) - fix(evm): Account + for (1) ERC20 transfers with tokens that return false success values instead of + throwing an error and (2) ERC20 transfers with other operations that don't bring + about the expected resulting balance for the transfer recipient. +- [#2091](https://github.com/NibiruChain/nibiru/pull/2091) - feat(evm): add fun token creation fee validation +- [#2093](https://github.com/NibiruChain/nibiru/pull/2093) - feat(evm): gas usage in precompiles: limits, local gas meters +- [#2092](https://github.com/NibiruChain/nibiru/pull/2092) - feat(evm): add validation for wasm multi message execution +- [#2094](https://github.com/NibiruChain/nibiru/pull/2094) - fix(evm): Following + from the changs in #2086, this pull request implements a new `JournalChange` + struct that saves a deep copy of the state multi store before each + state-modifying, Nibiru-specific precompiled contract is called (`OnRunStart`). + Additionally, we commit the `StateDB` there as well. This guarantees that the + non-EVM and EVM state will be in sync even if there are complex, multi-step + Ethereum transactions, such as in the case of an EthereumTx that influences the + `StateDB`, then calls a precompile that also changes non-EVM state, and then EVM + reverts inside of a try-catch. +- [#2095](https://github.com/NibiruChain/nibiru/pull/2095) - fix(evm): This + change records NIBI (ether) transfers on the `StateDB` during precompiled + contract calls using the `NibiruBankKeeper`, which is struct extension of + the `bankkeeper.BaseKeeper` that is used throughout Nibiru. + The `NibiruBankKeeper` holds a reference to the current EVM `StateDB` and records + balance changes in wei as journal changes automatically. This guarantees that + commits and reversions of the `StateDB` do not misalign with the state of the + Bank module. This code change uses the `NibiruBankKeeper` on all modules that + depend on x/bank, such as the EVM and Wasm modules. +- [#2097](https://github.com/NibiruChain/nibiru/pull/2097) - feat(evm): Add new query to get dated price from the oracle precompile +- [#2098](https://github.com/NibiruChain/nibiru/pull/2098) - test(evm): statedb + tests for race conditions within funtoken precompile +- [#2100](https://github.com/NibiruChain/nibiru/pull/2100) - refactor: cleanup statedb and precompile sections +- [#2098](https://github.com/NibiruChain/nibiru/pull/2098) - test(evm): statedb tests for race conditions within funtoken precompile +- [#2090](https://github.com/NibiruChain/nibiru/pull/2090) - fix(evm): Account +for (1) ERC20 transfers with tokens that return false success values instead of +throwing an error and (2) ERC20 transfers with other operations that don't bring +about the expected resulting balance for the transfer recipient. +- [#2092](https://github.com/NibiruChain/nibiru/pull/2092) - feat(evm): add validation for wasm multi message execution +- [#2101](https://github.com/NibiruChain/nibiru/pull/2101) - fix(evm): tx receipt proper marshalling +- [#2105](https://github.com/NibiruChain/nibiru/pull/2105) - test(evm): precompile call with revert +- [#2106](https://github.com/NibiruChain/nibiru/pull/2106) - chore: scheduled basic e2e tests for evm testnet endpoint +- [#2107](https://github.com/NibiruChain/nibiru/pull/2107) - feat(evm-funtoken-precompile): Implement methods: balance, bankBalance, whoAmI +- [#2108](https://github.com/NibiruChain/nibiru/pull/2108) - fix(evm): removed deprecated root key from eth_getTransactionReceipt +- [#2110](https://github.com/NibiruChain/nibiru/pull/2110) - fix(evm): Restore StateDB to its state prior to ApplyEvmMsg call to ensure deterministic gas usage. This fixes an issue where the StateDB pointer field in NibiruBankKeeper was being updated during readonly query endpoints like eth_estimateGas, leading to non-deterministic gas usage in subsequent transactions. +- [#2111](https://github.com/NibiruChain/nibiru/pull/2111) - fix: e2e-evm-cron.yml +- [#2114](https://github.com/NibiruChain/nibiru/pull/2114) - fix(evm): make gas cost zero in conditional bank keeper flow +- [#2116](https://github.com/NibiruChain/nibiru/pull/2116) - fix(precompile-funtoken.go): Fixes a bug where the err != nil check is missing in the bankBalance precompile method +- [#2117](https://github.com/NibiruChain/nibiru/pull/2117) - fix(oracle): The + timestamps resulting from ctx.WithBlock\* don't actually correspond to the block + header information from specified blocks in the chain's history, so the oracle + exchange rates need a way to correctly retrieve this information. This change + fixes that discrepancy, giving the expected block timestamp for the EVM's oracle + precompiled contract. The change also simplifies and corrects the code in x/oracle. + +#### Nibiru EVM | Before Audit 1 - 2024-10-18 + +- [#1837](https://github.com/NibiruChain/nibiru/pull/1837) - feat(eth): protos, eth types, and evm module types +- [#1838](https://github.com/NibiruChain/nibiru/pull/1838) - feat(eth): Go-ethereum, crypto, encoding, and unit tests for evm/types +- [#1841](https://github.com/NibiruChain/nibiru/pull/1841) - feat(eth): Collections encoders for bytes, Ethereum addresses, and Ethereum hashes +- [#1855](https://github.com/NibiruChain/nibiru/pull/1855) - feat(eth-pubsub): Implement in-memory EventBus for real-time topic management and event distribution +- [#1856](https://github.com/NibiruChain/nibiru/pull/1856) - feat(eth-rpc): Conversion types and functions between Ethereum txs and blocks and Tendermint ones. +- [#1861](https://github.com/NibiruChain/nibiru/pull/1861) - feat(eth-rpc): RPC backend, Ethereum tracer, KV indexer, and RPC APIs +- [#1869](https://github.com/NibiruChain/nibiru/pull/1869) - feat(eth): Module and start of keeper tests +- [#1871](https://github.com/NibiruChain/nibiru/pull/1871) - feat(evm): app config and json-rpc +- [#1873](https://github.com/NibiruChain/nibiru/pull/1873) - feat(evm): keeper collections and grpc query impls for EthAccount, NibiruAccount +- [#1883](https://github.com/NibiruChain/nibiru/pull/1883) - feat(evm): keeper logic, Ante handlers, EthCall, and EVM transactions. +- [#1887](https://github.com/NibiruChain/nibiru/pull/1887) - test(evm): eth api integration test suite +- [#1889](https://github.com/NibiruChain/nibiru/pull/1889) - feat: implemented basic evm tx methods +- [#1895](https://github.com/NibiruChain/nibiru/pull/1895) - refactor(geth): Reference go-ethereum as a submodule for easier change tracking with upstream +- [#1901](https://github.com/NibiruChain/nibiru/pull/1901) - test(evm): more e2e test contracts for edge cases +- [#1907](https://github.com/NibiruChain/nibiru/pull/1907) - test(evm): grpc_query full coverage +- [#1909](https://github.com/NibiruChain/nibiru/pull/1909) - chore(evm): set is_london true by default and removed from config +- [#1911](https://github.com/NibiruChain/nibiru/pull/1911) - chore(evm): simplified config by removing old eth forks +- [#1912](https://github.com/NibiruChain/nibiru/pull/1912) - test(evm): unit tests for evm_ante +- [#1914](https://github.com/NibiruChain/nibiru/pull/1914) - refactor(evm): Remove dead code and document non-EVM ante handler +- [#1917](https://github.com/NibiruChain/nibiru/pull/1917) - test(e2e-evm): TypeScript support. Type generation from compiled contracts. Formatter for TS code. +- [#1922](https://github.com/NibiruChain/nibiru/pull/1922) - feat(evm): tracer option is read from the config. +- [#1936](https://github.com/NibiruChain/nibiru/pull/1936) - feat(evm): EVM fungible token protobufs and encoding tests +- [#1947](https://github.com/NibiruChain/nibiru/pull/1947) - fix(evm): fix FunToken state marshalling +- [#1949](https://github.com/NibiruChain/nibiru/pull/1949) - feat(evm): add fungible token mapping queries +- [#1950](https://github.com/NibiruChain/nibiru/pull/1950) - feat(evm): Tx to create FunToken mapping from ERC20, contract embeds, and ERC20 queries. +- [#1956](https://github.com/NibiruChain/nibiru/pull/1956) - feat(evm): msg to send bank coin to erc20 +- [#1958](https://github.com/NibiruChain/nibiru/pull/1958) - chore(evm): wiped deprecated evm apis: miner, personal +- [#1959](https://github.com/NibiruChain/nibiru/pull/1959) - feat(evm): Add precompile to the EVM that enables transfers of ERC20 tokens to "nibi" accounts as regular Ethereum transactions +- [#1960](https://github.com/NibiruChain/nibiru/pull/1960) - test(network): graceful cleanup for more consistent CI runs +- [#1961](https://github.com/NibiruChain/nibiru/pull/1961) - chore(test): reverted funtoken precompile test back to the isolated state +- [#1962](https://github.com/NibiruChain/nibiru/pull/1962) - chore(evm): code cleanup, unused code, typos, styles, warnings +- [#1963](https://github.com/NibiruChain/nibiru/pull/1963) - feat(evm): Deduct a fee during the creation of a FunToken mapping. Implemented by `deductCreateFunTokenFee` inside of the `eth.evm.v1.MsgCreateFunToken` transaction. +- [#1965](https://github.com/NibiruChain/nibiru/pull/1965) - refactor(evm): remove evm post-processing hooks +- [#1966](https://github.com/NibiruChain/nibiru/pull/1966) - refactor(evm): clean up AnteHandler setup +- [#1967](https://github.com/NibiruChain/nibiru/pull/1967) - feat(evm): export genesis +- [#1968](https://github.com/NibiruChain/nibiru/pull/1968) - refactor(evm): funtoken events, cli commands and queries +- [#1970](https://github.com/NibiruChain/nibiru/pull/1970) - refactor(evm): move evm antehandlers to separate package. Remove "gosdk/sequence_test.go", which causes a race condition in CI. +- [#1971](https://github.com/NibiruChain/nibiru/pull/1971) - feat(evm): typed events for contract creation, contract execution and transfer +- [#1973](https://github.com/NibiruChain/nibiru/pull/1973) - chore(appconst): Add chain IDs ending in "3" to the "knownEthChainIDMap". This makes it possible to use devnet 3 and testnet 3. +- [#1976](https://github.com/NibiruChain/nibiru/pull/1976) - refactor(evm): unique chain ids for all networks +- [#1977](https://github.com/NibiruChain/nibiru/pull/1977) - fix(localnet): rolled back change of evm validator address with cosmos derivation path +- [#1979](https://github.com/NibiruChain/nibiru/pull/1979) - refactor(db): use pebbledb as the default db in integration tests +- [#1981](https://github.com/NibiruChain/nibiru/pull/1981) - fix(evm): remove isCheckTx() short circuit on `AnteDecVerifyEthAcc` +- [#1982](https://github.com/NibiruChain/nibiru/pull/1982) - feat(evm): add GlobalMinGasPrices +- [#1983](https://github.com/NibiruChain/nibiru/pull/1983) - chore(evm): remove ExtensionOptionsWeb3Tx and ExtensionOptionDynamicFeeTx +- [#1984](https://github.com/NibiruChain/nibiru/pull/1984) - refactor(evm): embeds +- [#1985](https://github.com/NibiruChain/nibiru/pull/1985) - feat(evm)!: Use atto denomination for the wei units in the EVM so that NIBI is "ether" to clients. Only micronibi (unibi) amounts can be transferred. All clients follow the constraint equation, 1 ether == 1 NIBI == 10^6 unibi == 10^18 wei. +- [#1986](https://github.com/NibiruChain/nibiru/pull/1986) - feat(evm): Combine both account queries into "/eth.evm.v1.Query/EthAccount", accepting both nibi-prefixed Bech32 addresses and Ethereum-type hexadecimal addresses as input. +- [#1989](https://github.com/NibiruChain/nibiru/pull/1989) - refactor(evm): simplify evm module address +- [#1996](https://github.com/NibiruChain/nibiru/pull/1996) - perf(evm-keeper-precompile): implement sorted map for `k.precompiles` to remove dead code +- [#1997](https://github.com/NibiruChain/nibiru/pull/1997) - refactor(evm): Remove unnecessary params: "enable_call", "enable_create". +- [#2000](https://github.com/NibiruChain/nibiru/pull/2000) - refactor(evm): simplify ERC-20 keeper methods +- [#2001](https://github.com/NibiruChain/nibiru/pull/2001) - refactor(evm): simplify FunToken methods and tests +- [#2002](https://github.com/NibiruChain/nibiru/pull/2002) - feat(evm): Add the account query to the EVM command. Cover the CLI with tests. +- [#2003](https://github.com/NibiruChain/nibiru/pull/2003) - fix(evm): fix FunToken conversions between Cosmos and EVM +- [#2004](https://github.com/NibiruChain/nibiru/pull/2004) - refactor(evm)!: replace `HexAddr` with `EIP55Addr` +- [#2006](https://github.com/NibiruChain/nibiru/pull/2006) - test(evm): e2e tests for eth\_\* endpoints +- [#2008](https://github.com/NibiruChain/nibiru/pull/2008) - refactor(evm): clean up precompile setups +- [#2013](https://github.com/NibiruChain/nibiru/pull/2013) - chore(evm): Set appropriate gas value for the required gas of the "IFunToken.sol" precompile. +- [#2014](https://github.com/NibiruChain/nibiru/pull/2014) - feat(evm): Emit block bloom event in EndBlock hook. +- [#2017](https://github.com/NibiruChain/nibiru/pull/2017) - fix(evm): Fix DynamicFeeTx gas cap parameters +- [#2019](https://github.com/NibiruChain/nibiru/pull/2019) - chore(evm): enabled debug rpc api on localnet. +- [#2020](https://github.com/NibiruChain/nibiru/pull/2020) - test(evm): e2e tests for debug namespace +- [#2022](https://github.com/NibiruChain/nibiru/pull/2022) - feat(evm): debug_traceCall method implemented +- [#2023](https://github.com/NibiruChain/nibiru/pull/2023) - fix(evm)!: adjusted generation and parsing of the block bloom events +- [#2030](https://github.com/NibiruChain/nibiru/pull/2030) - refactor(eth/rpc): Delete unused code and improve logging in the eth and debug namespaces +- [#2031](https://github.com/NibiruChain/nibiru/pull/2031) - fix(evm): debug calls with custom tracer and tracer options +- [#2032](https://github.com/NibiruChain/nibiru/pull/2032) - feat(evm): ante handler to prohibit authz grant evm messages +- [#2039](https://github.com/NibiruChain/nibiru/pull/2039) - refactor(rpc-backend): remove unnecessary interface code +- [#2044](https://github.com/NibiruChain/nibiru/pull/2044) - feat(evm): evm tx indexer service implemented +- [#2045](https://github.com/NibiruChain/nibiru/pull/2045) - test(evm): backend tests with test network and real txs +- [#2053](https://github.com/NibiruChain/nibiru/pull/2053) - refactor(evm): converted untyped event to typed and cleaned up +- [#2054](https://github.com/NibiruChain/nibiru/pull/2054) - feat(evm-precompile): Precompile for one-way EVM calls to invoke/execute Wasm contracts. +- [#2060](https://github.com/NibiruChain/nibiru/pull/2060) - fix(evm-precompiles): add assertNumArgs validation +- [#2056](https://github.com/NibiruChain/nibiru/pull/2056) - feat(evm): add oracle precompile +- [#2065](https://github.com/NibiruChain/nibiru/pull/2065) - refactor(evm)!: Refactor out dead code from the evm.Params +- [#2135](https://github.com/NibiruChain/nibiru/pull/2135) - feat(evm): add precompile for calling bank to evm from evm + +### State Machine Breaking (Other) + +#### For next mainnet version + +- [#1766](https://github.com/NibiruChain/nibiru/pull/1766) - refactor(app-wasmext)!: remove wasmbinding `CosmosMsg::Custom` bindings. +- [#1776](https://github.com/NibiruChain/nibiru/pull/1776) - feat(inflation): make inflation params a collection and add commands to update them +- [#1872](https://github.com/NibiruChain/nibiru/pull/1872) - chore(math): use cosmossdk.io/math to replace sdk types +- [#1874](https://github.com/NibiruChain/nibiru/pull/1874) - chore(proto): remove the proto stringer as per Cosmos SDK migration guidelines +- [#1932](https://github.com/NibiruChain/nibiru/pull/1932) - fix(gosdk): fix keyring import functions + +#### Dapp modules: perp, spot, oracle, etc + +- [#1573](https://github.com/NibiruChain/nibiru/pull/1573) - feat(perp): Close markets and compute settlement price +- [#1632](https://github.com/NibiruChain/nibiru/pull/1632) - feat(perp): Add settle position transaction +- [#1656](https://github.com/NibiruChain/nibiru/pull/1656) - feat(perp): Make the collateral denom a stateful collections.Item +- [#1663](https://github.com/NibiruChain/nibiru/pull/1663) - feat(perp): Add volume based rebates +- [#1669](https://github.com/NibiruChain/nibiru/pull/1669) - feat(perp): add query to get collateral metadata +- [#1677](https://github.com/NibiruChain/nibiru/pull/1677) - fix(perp): make Gen_market set initial perp versions +- [#1680](https://github.com/NibiruChain/nibiru/pull/1680) - feat(perp): MsgShiftPegMultiplier, MsgShiftSwapInvariant. +- [#1683](https://github.com/NibiruChain/nibiru/pull/1683) - feat(perp): Add `StartDnREpoch` to `AfterEpochEnd` hook +- [#1686](https://github.com/NibiruChain/nibiru/pull/1686) - test(perp): add more tests for perp module msg server for DnR +- [#1687](https://github.com/NibiruChain/nibiru/pull/1687) - chore(wasmbinding): delete CustomQuerier since we have QueryRequest::Stargate now +- [#1705](https://github.com/NibiruChain/nibiru/pull/1705) - feat(perp): Add oracle pair to market object +- [#1718](https://github.com/NibiruChain/nibiru/pull/1718) - fix(perp): fees does not require additional funds +- [#1734](https://github.com/NibiruChain/nibiru/pull/1734) - feat(perp): MsgDonateToPerpFund sudo call as part of #1642 +- [#1749](https://github.com/NibiruChain/nibiru/pull/1749) - feat(perp): move close market from Wasm Binding to MsgCloseMarket +- [#1752](https://github.com/NibiruChain/nibiru/pull/1752) - feat(oracle): MsgEditOracleParams sudo tx msg as part of #1642 +- [#1755](https://github.com/NibiruChain/nibiru/pull/1755) - feat(oracle): Add more events on validator's performance +- [#1764](https://github.com/NibiruChain/nibiru/pull/1764) - fix(perp): make updateswapinvariant aware of total short supply to avoid panics +- [#1710](https://github.com/NibiruChain/nibiru/pull/1710) - refactor(perp): Clean and organize module errors for x/perp + +### Non-breaking/Compatible Improvements + +- [#1893](https://github.com/NibiruChain/nibiru/pull/1893) - feat(gosdk): migrate Go-sdk into the Nibiru blockchain repo. +- [#1899](https://github.com/NibiruChain/nibiru/pull/1899) - build(deps): cometbft v0.37.5, cosmos-sdk v0.47.11, proto-builder v0.14.0 +- [#1913](https://github.com/NibiruChain/nibiru/pull/1913) - fix(tests): race condition from heavy Network tests +- [#1992](https://github.com/NibiruChain/nibiru/pull/1992) - chore: enabled grpc for localnet +- [#1999](https://github.com/NibiruChain/nibiru/pull/1999) - chore: update nibi go package version to v2 +- [#2050](https://github.com/NibiruChain/nibiru/pull/2050) - refactor(oracle): remove unused code and collapse empty client/cli directory + +### Dependencies + +- Bump `github.com/grpc-ecosystem/grpc-gateway/v2` from 2.18.1 to 2.19.1 ([#1767](https://github.com/NibiruChain/nibiru/pull/1767), [#1782](https://github.com/NibiruChain/nibiru/pull/1782)) +- Bump `robinraju/release-downloader` from 1.8 to 1.11 ([#1783](https://github.com/NibiruChain/nibiru/pull/1783), [#1839](https://github.com/NibiruChain/nibiru/pull/1839), [#1948](https://github.com/NibiruChain/nibiru/pull/1948)) +- Bump `github.com/prometheus/client_golang` from 1.17.0 to 1.18.0 ([#1750](https://github.com/NibiruChain/nibiru/pull/1750)) +- Bump `golang.org/x/crypto` from 0.15.0 to 0.31.0 ([#1724](https://github.com/NibiruChain/nibiru/pull/1724), [#1843](https://github.com/NibiruChain/nibiru/pull/1843), [#2123](https://github.com/NibiruChain/nibiru/pull/2123)) +- Bump `github.com/holiman/uint256` from 1.2.3 to 1.2.4 ([#1730](https://github.com/NibiruChain/nibiru/pull/1730)) +- Bump `github.com/dvsekhvalnov/jose2go` from 1.5.0 to 1.6.0 ([#1733](https://github.com/NibiruChain/nibiru/pull/1733)) +- Bump `github.com/spf13/cast` from 1.5.1 to 1.6.0 ([#1689](https://github.com/NibiruChain/nibiru/pull/1689)) +- Bump `cosmossdk.io/math` from 1.1.2 to 1.4.0 ([#1676](https://github.com/NibiruChain/nibiru/pull/1676), [#2115](https://github.com/NibiruChain/nibiru/pull/2115)) +- Bump `github.com/grpc-ecosystem/grpc-gateway/v2` from 2.18.0 to 2.18.1 ([#1675](https://github.com/NibiruChain/nibiru/pull/1675)) +- Bump `actions/setup-go` from 4 to 5 ([#1696](https://github.com/NibiruChain/nibiru/pull/1696)) +- Bump `golang` from 1.19 to 1.21 ([#1698](https://github.com/NibiruChain/nibiru/pull/1698)) +- [#1678](https://github.com/NibiruChain/nibiru/pull/1678) - chore(deps): collections to v0.4.0 for math.Int value encoder +- Bump `golang.org/x/net` from 0.0.0-20220607020251-c690dde0001d to 0.33.0 ([#1849](https://github.com/NibiruChain/nibiru/pull/1849), [#2175](https://github.com/NibiruChain/nibiru/pull/2175)) +- Bump `golang.org/x/net` from 0.20.0 to 0.23.0 ([#1850](https://github.com/NibiruChain/nibiru/pull/1850)) +- Bump `github.com/supranational/blst` from 0.3.8-0.20220526154634-513d2456b344 to 0.3.11 ([#1851](https://github.com/NibiruChain/nibiru/pull/1851)) +- Bump `golangci/golangci-lint-action` from 4 to 6 ([#1854](https://github.com/NibiruChain/nibiru/pull/1854), [#1867](https://github.com/NibiruChain/nibiru/pull/1867)) +- Bump `github.com/hashicorp/go-getter` from 1.7.1 to 1.7.5 ([#1858](https://github.com/NibiruChain/nibiru/pull/1858), [#1938](https://github.com/NibiruChain/nibiru/pull/1938)) +- Bump `github.com/btcsuite/btcd` from 0.23.3 to 0.24.2 ([#1862](https://github.com/NibiruChain/nibiru/pull/1862), [#2070](https://github.com/NibiruChain/nibiru/pull/2070)) +- Bump `pozetroninc/github-action-get-latest-release` from 0.7.0 to 0.8.0 ([#1863](https://github.com/NibiruChain/nibiru/pull/1863)) +- Bump `bufbuild/buf-setup-action` from 1.30.1 to 1.47.2 ([#1891](https://github.com/NibiruChain/nibiru/pull/1891), [#1900](https://github.com/NibiruChain/nibiru/pull/1900), [#1923](https://github.com/NibiruChain/nibiru/pull/1923), [#1972](https://github.com/NibiruChain/nibiru/pull/1972), [#1974](https://github.com/NibiruChain/nibiru/pull/1974), [#1988](https://github.com/NibiruChain/nibiru/pull/1988), [#2043](https://github.com/NibiruChain/nibiru/pull/2043), [#2057](https://github.com/NibiruChain/nibiru/pull/2057), [#2062](https://github.com/NibiruChain/nibiru/pull/2062), [#2069](https://github.com/NibiruChain/nibiru/pull/2069), [#2102](https://github.com/NibiruChain/nibiru/pull/2102), [#2113](https://github.com/NibiruChain/nibiru/pull/2113)) +- Bump `axios` from 1.7.3 to 1.7.4 ([#2016](https://github.com/NibiruChain/nibiru/pull/2016)) +- Bump `github.com/CosmWasm/wasmvm` from 1.5.0 to 1.5.5 ([#2047](https://github.com/NibiruChain/nibiru/pull/2047)) +- Bump `docker/build-push-action` from 5 to 6 ([#1924](https://github.com/NibiruChain/nibiru/pull/1924)) +- Bump `codecov/codecov-action` from 4 to 5 ([#2112](https://github.com/NibiruChain/nibiru/pull/2112)) +- Bump `undici` from 5.28.4 to 5.28.5 ([#2174](https://github.com/NibiruChain/nibiru/pull/2174)) ## [v1.5.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.5.0) - 2024-06-21 Nibiru v1.5.0 enables IBC CosmWasm smart contracts. -* [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.5.0)] -* [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.5.0)] +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.5.0)] +- [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.5.0)] ### Features -* [#1931](https://github.com/NibiruChain/nibiru/pull/1931) - feat(ibc): add `wasm` route to IBC router +- [#1931](https://github.com/NibiruChain/nibiru/pull/1931) - feat(ibc): add `wasm` route to IBC router ## [v1.4.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.4.0) - 2024-06-04 Nibiru v1.4.0 adds PebbleDB support and increases the wasm contract size limit to 3MB. -* [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.4.0)] -* [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.4.0)] +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.4.0)] +- [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.4.0)] ### State Machine Breaking -* [#1906](https://github.com/NibiruChain/nibiru/pull/1906) - feat(wasm): increase contract size limit to 3MB +- [#1906](https://github.com/NibiruChain/nibiru/pull/1906) - feat(wasm): increase contract size limit to 3MB ### Features -* [#1818](https://github.com/NibiruChain/nibiru/pull/1818) - feat: add pebbledb support -* [#1908](https://github.com/NibiruChain/nibiru/pull/1908) - chore: make pebbledb the default db backend +- [#1818](https://github.com/NibiruChain/nibiru/pull/1818) - feat: add pebbledb support +- [#1908](https://github.com/NibiruChain/nibiru/pull/1908) - chore: make pebbledb the default db backend +- ## [v1.3.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.3.0) - 2024-05-07 Nibiru v1.3.0 adds interchain accounts. -* [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.3.0)] -* [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.3.0)] - -### Features - -* [#1820](https://github.com/NibiruChain/nibiru/pull/1820) - feat: add interchain accounts - -### Bug Fixes - -* [#1864](https://github.com/NibiruChain/nibiru/pull/1864) - fix(ica): add ICA controller stack - -### Improvements - -* [#1859](https://github.com/NibiruChain/nibiru/pull/1859) - refactor(oracle): add oracle slashing events - -## [v1.2.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.2.0) - 2024-03-28 - -Nibiru v1.2.0 adds a burn method to the x/inflation module that allows senders to burn tokens. - -* [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.2.0)] -* [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.2.0)] - -### Features - -* [#1832](https://github.com/NibiruChain/nibiru/pull/1832) - feat(tokenfactory): add burn method for native tokens - -## [v1.1.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.1.0) - 2024-03-19 - -Nibiru v1.1.0 is the minor release that adds inflation to the network. - -* [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.1.0)] -* [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.1.0)] - -### Features - -* [#1670](https://github.com/NibiruChain/nibiru/pull/1670) - feat(inflation): Make inflation polynomial -* [#1682](https://github.com/NibiruChain/nibiru/pull/1682) - feat!: add upgrade handler for v1.1.0 -* [#1776](https://github.com/NibiruChain/nibiru/pull/1776) - feat(inflation): make inflation params a collection and add commands to update them -* [#1795](https://github.com/NibiruChain/nibiru/pull/1795) - feat(inflation): add inflation tx cmds - -### Bug Fixes - -* [#1688](https://github.com/NibiruChain/nibiru/pull/1688) - fix(inflation): make default inflation allocation follow tokenomics -* [#1706](https://github.com/NibiruChain/nibiru/pull/706) - fix: `v1.1.0` upgrade handler -* [#1786](https://github.com/NibiruChain/nibiru/pull/1786) - fix(inflation): fix inflation off-by 2 error -* [#1796](https://github.com/NibiruChain/nibiru/pull/1796) - fix(inflation): fix num skipped epoch when inflation is added to an existing chain -* [#1797](https://github.com/NibiruChain/nibiru/pull/1797) - fix(inflation): fix num skipped epoch updates logic -* [#1804](https://github.com/NibiruChain/nibiru/pull/1804) - fix(inflation): update default parameters - -### Improvements - -* [#1695](https://github.com/NibiruChain/nibiru/pull/1695) - feat(inflation): add events for inflation distribution -* [#1712](https://github.com/NibiruChain/nibiru/pull/1712) - refactor(inflation): turn inflation off by default -* [#1792](https://github.com/NibiruChain/nibiru/pull/1792) - fix(inflation): uncomment legacy amino register on app module basic -* [#1799](https://github.com/NibiruChain/nibiru/pull/1799) refactor,docs(inflation): Document everything + delete unused code. Make perp and spot optional features in localnet.sh - -## [v1.0.3](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.3) - 2024-03-18 - -### Fix - -* [#1816](https://github.com/NibiruChain/nibiru/pull/1816) - fix(ibc): fix ibc transaction from wasm contract - -### CLI - -* [#1731](https://github.com/NibiruChain/nibiru/pull/1731) - feat(cli): add cli command to decode stargate base64 messages -* [#1754](https://github.com/NibiruChain/nibiru/pull/1754) - refactor(decode-base64): clean code improvements and fn docs - -## [v1.0.2](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.2) - 2024-03-03 - -### Dependencies - -* [65c06ba](https://github.com/NibiruChain/nibiru/commit/65c06ba774c260ece942131ad7a93de0e162266e) - Bump `cosmos-sdk` to v0.47.10 - -## [v1.0.1](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.1) - 2024-02-09 - -### Dependencies - -* [#1778](https://github.com/NibiruChain/nibiru/pull/1778) - chore: bump librocksdb to v8.9.1 - -## [v1.0.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.0) - -### Features - -* [#1596](https://github.com/NibiruChain/nibiru/pull/1596) - epic(tokenfactory): State transitions, collections, genesis import and export, and app wiring -* [#1607](https://github.com/NibiruChain/nibiru/pull/1607) - Token factory transaction messages for CreateDenom, ChangeAdmin, and UpdateModuleParams -* [#1620](https://github.com/NibiruChain/nibiru/pull/1620) - Token factory transaction messages for Mint and Burn -* [#1573](https://github.com/NibiruChain/nibiru/pull/1573) - feat(perp): Close markets and compute settlement price - -### State Machine Breaking - -* [#1609](https://github.com/NibiruChain/nibiru/pull/1609) - refactor(app)!: Remove x/stablecoin module. -* [#1613](https://github.com/NibiruChain/nibiru/pull/1613) - feat(app)!: enforce min commission by changing default and genesis validation -* [#1615](https://github.com/NibiruChain/nibiru/pull/1613) - feat(ante)!: Ante handler to add a maximum commission rate of 25% for validators. -* [#1616](https://github.com/NibiruChain/nibiru/pull/1616) - fix(app)!: Add custom wasm snapshotter for proper state exports -* [#1617](https://github.com/NibiruChain/nibiru/pull/1617) - fix(app)!: non-nil snapshot manager is not guaranteed in testapp -* [#1645](https://github.com/NibiruChain/nibiru/pull/1645) - fix(tokenfactory)!: token supply in bank keeper must be correct after MsgBurn. -* [#1646](https://github.com/NibiruChain/nibiru/pull/1646) - feat(wasmbinding)!: whitelisted stargate queries for QueryRequest::Stargate: auth, bank, gov, tokenfactory, epochs, inflation, oracle, sudo, devgas -* [#1667](https://github.com/NibiruChain/nibiru/pull/1667) - chore(inflation)!: unwire x/inflation - -### Improvements - -* [#1610](https://github.com/NibiruChain/nibiru/pull/1610) - refactor(app): Simplify app.go with less redundant imports using struct embedding. -* [#1614](https://github.com/NibiruChain/nibiru/pull/1614) - refactor(proto): Use explicit namespacing on proto imports for #1608 -* [#1630](https://github.com/NibiruChain/nibiru/pull/1630) - refactor(wasm): clean up wasmbinding/ folder structure -* [#1631](https://github.com/NibiruChain/nibiru/pull/1631) - fix(.goreleaser.yml): Load version for wasmvm dynamically. -* [#1638](https://github.com/NibiruChain/nibiru/pull/1638) - test(tokenfactory): integration test core logic with a real smart contract using `nibiru-std` -* [#1659](https://github.com/NibiruChain/nibiru/pull/1659) - refactor(oracle): curate oracle default whitelist - -### Dependencies - -* Bump `github.com/prometheus/client_golang` from 1.16.0 to 1.17.0 ([#1605](https://github.com/NibiruChain/nibiru/pull/1605)) - -* Bump `bufbuild/buf-setup-action` from 1.26.1 to 1.27.1 ([#1624](https://github.com/NibiruChain/nibiru/pull/1624), [#1641](https://github.com/NibiruChain/nibiru/pull/1641)) -* Bump `stefanzweifel/git-auto-commit-action` from 4 to 5 ([#1625](https://github.com/NibiruChain/nibiru/pull/1625)) -* Bump `github.com/CosmWasm/wasmvm` from 1.4.0 to 1.5.0 ([#1629](https://github.com/NibiruChain/nibiru/pull/1629), [#1657](https://github.com/NibiruChain/nibiru/pull/1657)) -* Bump `google.golang.org/grpc` from 1.58.2 to 1.59.0 ([#1633](https://github.com/NibiruChain/nibiru/pull/1633), [#1643](https://github.com/NibiruChain/nibiru/pull/1643)) -* Bump `golang.org/x/net` from 0.12.0 to 0.17.0 ([#1634](https://github.com/NibiruChain/nibiru/pull/1634)) -* Bump `github.com/cosmos/ibc-go/v7` from 7.3.0 to 7.3.1 ([#1647](https://github.com/NibiruChain/nibiru/pull/1647)) -* Bump `github.com/CosmWasm/wasmd` from 0.40.2 to 0.43.0 ([#1660](https://github.com/NibiruChain/nibiru/pull/1660)) -* Bump `github.com/CosmWasm/wasmd` from 0.43.0 to 0.44.0 ([#1666](https://github.com/NibiruChain/nibiru/pull/1666)) - -### Bug Fixes - -* [#1606](https://github.com/NibiruChain/nibiru/pull/1606) - fix(perp): emit `MarketUpdatedEvent` in the absence of index price -* [#1649](https://github.com/NibiruChain/nibiru/pull/1649) - fix(ledger): fix ledger for newer macos versions -* [#1655](https://github.com/NibiruChain/nibiru/pull/1655) - fix(inflation): inflate NIBI correctly to strategic treasury account - -## [v0.21.10] +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.3.0)] +- [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.3.0)] ### Features -* [#1575](https://github.com/NibiruChain/nibiru/pull/1575) - feat(perp): Add trader volume tracking -* [#1463](https://github.com/NibiruChain/nibiru/pull/1463) - feat(oracle): add genesis pricefeeder delegation -* [#1479](https://github.com/NibiruChain/nibiru/pull/1479) - feat(perp): implement `PartialClose` -* [#1498](https://github.com/NibiruChain/nibiru/pull/1498) - feat: add cli to change root sudo command -* [#1501](https://github.com/NibiruChain/nibiru/pull/1501) - feat(localnet.sh): (1) Make it possible to run while offline. (2) Implement --no-build option to use the script with the current `nibid` installed. -* [#1501](https://github.com/NibiruChain/nibiru/pull/1501) - feat(proto): add Python buf generation logic for py-sdk -* [#1503](https://github.com/NibiruChain/nibiru/pull/1503) - feat(wasm): add Oracle Exchange Rate query for wasm -* [#1543](https://github.com/NibiruChain/nibiru/pull/1543) - epic(devgas): devgas module for incentivizing smart contract -* [#1559](https://github.com/NibiruChain/nibiru/pull/1559) - feat: add versions to markets to allow to disable them -* [#1585](https://github.com/NibiruChain/nibiru/pull/1585) - feat: include flag versioned in query markets to allow to query disabled markets -* [#1594](https://github.com/NibiruChain/nibiru/pull/1594) - feat: add user discounts -* [#1463](https://github.com/NibiruChain/nibiru/pull/1463) - feat(oracle): add genesis pricefeeder delegation -* [#1479](https://github.com/NibiruChain/nibiru/pull/1479) - feat(perp): implement `PartialClose` -* [#1498](https://github.com/NibiruChain/nibiru/pull/1498) - feat: add cli to change root sudo command -* [#1501](https://github.com/NibiruChain/nibiru/pull/1501) - feat(localnet.sh): (1) Make it possible to run while offline. (2) Implement --no-build option to use the script with the current `nibid` installed. -* [#1501](https://github.com/NibiruChain/nibiru/pull/1501) - feat(proto): add Python buf generation logic for py-sdk -* [#1503](https://github.com/NibiruChain/nibiru/pull/1503) - feat(wasm): add Oracle Exchange Rate query for wasm -* [#1543](https://github.com/NibiruChain/nibiru/pull/1543) - epic(devgas): devgas module for incentivizing smart contract -* - -### Improvements - -* [#1466](https://github.com/NibiruChain/nibiru/pull/1466) - refactor(perp): `PositionLiquidatedEvent` -* [#1494](https://github.com/NibiruChain/nibiru/pull/1494) - feat: create cli to add sudo account into genesis -* [#1493](https://github.com/NibiruChain/nibiru/pull/1493) - fix(perp): allow `ClosePosition` when there is bad debt -* [#1500](https://github.com/NibiruChain/nibiru/pull/1500) - refactor(perp): clean up reverse market order mechanics -* [#1506](https://github.com/NibiruChain/nibiru/pull/1506) - refactor(oracle): Implement OrderedMap and use it for iterating through maps in x/oracle -* [#1502](https://github.com/NibiruChain/nibiru/pull/1502) - feat: add ledger build support -* [#1495](https://github.com/NibiruChain/nibiru/pull/1495) - feat: add genmsg module -* [#1517](https://github.com/NibiruChain/nibiru/pull/1517) - test: add more tests to x/hooks -* [#1518](https://github.com/NibiruChain/nibiru/pull/1518) - test: add more tests to x/perp -* [#1519](https://github.com/NibiruChain/nibiru/pull/1519) - test: add more tests to x/perp keeper -* [#1520](https://github.com/NibiruChain/nibiru/pull/1520) - feat(wasm): no op handler + tests with updated contracts -* [#1521](https://github.com/NibiruChain/nibiru/pull/1521) - test(sudo): increase unit test coverage -* [#1523](https://github.com/NibiruChain/nibiru/pull/1523) - chore: bump cosmos-sdk to v0.47.4 -* [#1527](https://github.com/NibiruChain/nibiru/pull/1527) - test(common): add docs for testutil and increase test coverage -* [#1536](https://github.com/NibiruChain/nibiru/pull/1536) - test(perp): add more tests to perp module and cli -* [#1533](https://github.com/NibiruChain/nibiru/pull/1533) - feat(perp): add differential fields to PositionChangedEvent -* [#1541](https://github.com/NibiruChain/nibiru/pull/1541) - feat(perp): add clamp to premium fractions -* [#1555](https://github.com/NibiruChain/nibiru/pull/1555) - feat(devgas): Convert legacy ABCI events to typed proto events -* [#1558](https://github.com/NibiruChain/nibiru/pull/1558) - feat(perp): paginated query to read the position store -* [#1554](https://github.com/NibiruChain/nibiru/pull/1554) - refactor: runs gofumpt formatter, which has nice conventions: go install mvdan.cc/gofumpt@latest - -### Bug Fixes - -* [#1459](https://github.com/NibiruChain/nibiru/pull/1459) - fix(spot): wire `x/spot` msgService into app router -* [#1467](https://github.com/NibiruChain/nibiru/pull/1467) - fix(oracle): make `calcTwap` safer -* [#1464](https://github.com/NibiruChain/nibiru/pull/1464) - fix(gov): wire legacy proposal handlers -* [#1565](https://github.com/NibiruChain/nibiru/pull/1565) - fix(oracle)!: Count vote omission as abstain for less slashing + more stability - -### State Machine Breaking - -* [#1473](https://github.com/NibiruChain/nibiru/pull/1473) - refactor(perp)!: rename `OpenPosition` to `MarketOrder` -* [#1477](https://github.com/NibiruChain/nibiru/pull/1477) - refactor(oracle)!: Move away from deprecated events to typed events in x/oracle - -### Dependencies - -* Bump `robinraju/release-downloader` from 1.6 to 1.8 (#1326) -* Bump `pozetroninc/github-action-get-latest-release` from 0.6.0 to 0.7.0 (#1325) -* Bump `technote-space/get-diff-action` from 4 to 6 (#1327) -* Bump `actions/setup-go` from 3 to 4 (#1324) -* Bump `github.com/docker/distribution` from 2.8.1+incompatible to 2.8.2+incompatible (#1339) -* Bump `github.com/CosmWasm/wasmvm` from 1.2.1 to 1.3.0 (#1354, #1507) -* Bump `github.com/spf13/cast` from 1.5.0 to 1.5.1 (#1358) -* Bump `github.com/stretchr/testify` from 1.8.2 to 1.8.4 (#1384, #1435) -* Bump `cosmossdk.io/math` from 1.0.0-beta.6 to 1.1.2 (#1394, [#1547](https://github.com/NibiruChain/nibiru/pull/1547)) -* Bump `google.golang.org/grpc` from 1.53.0 to 1.57.0 (#1395, #1437, #1443, #1497, [#1525](https://github.com/NibiruChain/nibiru/pull/1525)) -* Bump `github.com/gin-gonic/gin` from 1.8.1 to 1.9.1 (#1409) -* Bump `github.com/spf13/viper` from 1.15.0 to 1.16.0 (#1436) -* Bump `github.com/prometheus/client_golang` from 1.15.1 to 1.16.0 (#1431) -* Bump `github.com/cosmos/ibc-go/v7` from 7.1.0 to 7.3.0 (#1445, [#1562](https://github.com/NibiruChain/nibiru/pull/1562)) -* Bump `bufbuild/buf-setup-action` from 1.21.0 to 1.26.1 (#1449, #1469, #1505, #1510, [#1537](https://github.com/NibiruChain/nibiru/pull/1537), [#1540](https://github.com/NibiruChain/nibiru/pull/1540), [#1544](https://github.com/NibiruChain/nibiru/pull/1544)) -* Bump `google.golang.org/protobuf` from 1.30.0 to 1.31.0 (#1450) -* Bump `cosmossdk.io/errors` from 1.0.0-beta.7 to 1.0.0 (#1499) -* Bump `github.com/holiman/uint256` from 1.2.2 to 1.2.3 (#1504) -* Bump `actions/checkout` from 3 to 4 ([#1563](https://github.com/NibiruChain/nibiru/pull/1563)) - -### Breaking - -* [#1380](https://github.com/NibiruChain/nibiru/pull/1380) - feat(wasm): Add CreateMarket admin call for the controller contract -* [#1359](https://github.com/NibiruChain/nibiru/pull/1359) - feat(perp): Add InsuranceFundWithdraw admin call with corresponding smart contract -* [#1356](https://github.com/NibiruChain/nibiru/pull/1356) - build: Regress wasmvm (v1.1.1), tendermint (v0.34.24), and Cosmos-SDK (v0.45.14) dependencies -* [#1346](https://github.com/NibiruChain/nibiru/pull/1346) - build: Upgrade wasmvm (v1.2.1), tendermint (v0.34.26), and Cosmos-SDK (v0.45.14) dependencies -* [#1317](https://github.com/NibiruChain/nibiru/pull/1317) - feat(sudo): Implement and test CLI commands for tx and queries. -* [#1307](https://github.com/NibiruChain/nibiru/pull/1307) - feat(sudo): Create the x/sudo module + integration tests -* [#1299](https://github.com/NibiruChain/nibiru/pull/1299) - feat(wasm): Add peg shift bindings -* [#1292](https://github.com/NibiruChain/nibiru/pull/1292) - feat(wasm): Add module bindings for execute calls in x/perp: OpenPosition, ClosePosition, AddMargin, RemoveMargin. -* [#1287](https://github.com/NibiruChain/nibiru/pull/1287) - feat(wasm): Add module bindings for custom queries in x/perp: Reserves, AllMarkets, BasePrice, PremiumFraction, Metrics, PerpParams, PerpModuleAccounts -* [#1282](https://github.com/NibiruChain/nibiru/pull/1282) - feat(inflation)!: add inflation module -* [#1270](https://github.com/NibiruChain/nibiru/pull/1270) - refactor(proto)!: lint protos and standardize versioning -* [#1271](https://github.com/NibiruChain/nibiru/pull/1271) - refactor(perp)!: vpool → perp/amm #2 | imports and renames -* [#1269](https://github.com/NibiruChain/nibiru/pull/1269) - refactor(perp)!: merge x/util with x/perp -* [#1267](https://github.com/NibiruChain/nibiru/pull/1267) - refactor(perp)!: vpool → perp/amm #1 | Moves types, keeper, and cli -* [#1243](https://github.com/NibiruChain/nibiru/pull/1243) - feat(vpool): sqrt of liquidity depth tracked on pool -* [#1220](https://github.com/NibiruChain/nibiru/pull/1220) - feat: reduce gas fees when posting price -* [#1229](https://github.com/NibiruChain/nibiru/pull/1229) - feat: upgrade ibc to v4.2.0 and wasm v0.30.0 -* [#1254](https://github.com/NibiruChain/nibiru/pull/1254) - feat: add bias field into vpool -* [#1255](https://github.com/NibiruChain/nibiru/pull/1255) - feat: add peg multiplier field into vpool, which for now defaults to 1 -* [#1281](https://github.com/NibiruChain/nibiru/pull/1281) - feat: add peg multiplier to the pricing logic -* [#1291](https://github.com/NibiruChain/nibiru/pull/1291) - refactor(perp)!: add perp v2 state protos -* [#1296](https://github.com/NibiruChain/nibiru/pull/1296) - refactor(perp)!: update perp v2 state protos -* [#1298](https://github.com/NibiruChain/nibiru/pull/1298) - refactor(perp)!: remove `MaxOracleSpreadRatio` from Perpv2 -* [#1302](https://github.com/NibiruChain/nibiru/pull/1302) - refactor(oracle)!: price snapshot start time inclusive -* [#1301](https://github.com/NibiruChain/nibiru/pull/1301) - fix(epochs)!: correct epoch start time -* [#1304](https://github.com/NibiruChain/nibiru/pull/1304) - feat: db backend - rocksdb -* [#1305](https://github.com/NibiruChain/nibiru/pull/1305) - refactor(perp!): Remove unnecessary protos -* [#1312](https://github.com/NibiruChain/nibiru/pull/1312) - feat(wasm): wire depth shift handler to the wasm router -* [#1306](https://github.com/NibiruChain/nibiru/pull/1306) - feat(perp): complete perp v2 types -* [#1309](https://github.com/NibiruChain/nibiru/pull/1309) - feat: minimum swap amount set to $1 -* [#1336](https://github.com/NibiruChain/nibiru/pull/1336) - feat: move oracle params out of params subspace and onto the keeper -* [#1315](https://github.com/NibiruChain/nibiru/pull/1315) - feat: oracle rewards distribution every week -* [#1342](https://github.com/NibiruChain/nibiru/pull/1342) - feat(perp): market not enabled can only be used to close out existing positions -* [#1367](https://github.com/NibiruChain/nibiru/pull/1367) - feat: wire enable market to wasm -* [#1382](https://github.com/NibiruChain/nibiru/pull/1382) - refactor(perp)!: remove `perpv1` -* [#1385](https://github.com/NibiruChain/nibiru/pull/1385) - test(perp): add clearing house negative tests -* [#1388](https://github.com/NibiruChain/nibiru/pull/1388) - refactor(perp)!: idempotent position changed event -* [#1387](https://github.com/NibiruChain/nibiru/pull/1387) - feat: upgrade to Cosmos SDK v0.46.10 -* [#1413](https://github.com/NibiruChain/nibiru/pull/1413) - fix(perp): provide descriptive errors when all liquidations fail in MultiLiquidate -* [#1427](https://github.com/NibiruChain/nibiru/pull/1427) - refactor(perp)!: PositionChangedEvent `MarginToUser` -* [#1407](https://github.com/NibiruChain/nibiru/pull/1407) - feat!: upgrade to Cosmos SDK v0.47.3 - -### Improvements - -* [#1574](https://github.com/NibiruChain/nibiru/pull/1574) - chore(goreleaser): update wasmvm to v1.4.0 -* [#1463](https://github.com/NibiruChain/nibiru/pull/1463) - feat(oracle): add genesis pricefeeder delegation -* [#1466](https://github.com/NibiruChain/nibiru/pull/1466) - refactor(perp): `PositionLiquidatedEvent` -* [#1462](https://github.com/NibiruChain/nibiru/pull/1462) - fix(perp): Add pair to liquidation failed event. -* [#1424](https://github.com/NibiruChain/nibiru/pull/1424) - feat(perp): Add change type and exchanged margin to position changed events. -* [#1390](https://github.com/NibiruChain/nibiru/pull/1390) - fix(localnet.sh): Fix genesis market initialization + add force exits on failure -* [#1340](https://github.com/NibiruChain/nibiru/pull/1340) - feat(wasm): Enforce x/sudo contract permission checks on the shifter contract + integration tests -* [#1317](https://github.com/NibiruChain/nibiru/pull/1317) - feat(testutil): Use secp256k1 algo for private key generation in common/testutil. -* [#1322](https://gitub.com/NibiruChain/nibiru/pull/1322) - build(deps): Bumps github.com/armon/go-metrics from 0.4.0 to 0.4.1. -* [#1321](https://github.com/NibiruChain/nibiru/pull/1321) - build(deps): bump github.com/prometheus/client_golang from 1.15.0 to 1.15.1 -* [#1295](https://github.com/NibiruChain/nibiru/pull/1295) - refactor(app): Organize keepers, store keys, and module manager initialization in app.go -* [#1248](https://github.com/NibiruChain/nibiru/pull/1248) - refactor(common): Combine x/testutil and x/common/testutil. -* [#1245](https://github.com/NibiruChain/nibiru/pull/1245) - fix(localnet.sh): force localnet.sh to work even if Coingecko is down -* [#1230](https://github.com/NibiruChain/nibiru/pull/1230) - chore(deps): Bump github.com/holiman/uint256 from 1.2.1 to 1.2.2 -* [#1240](https://github.com/NibiruChain/nibiru/pull/1240) - ci: Test `make proto-gen` when the proto gen scripts or .proto files change -* [#1199](https://github.com/NibiruChain/nibiru/pull/1199) - chore(deps): bump golang.org/x/net from 0.4.0 to 0.7.0 -* [#1211](https://github.com/NibiruChain/nibiru/pull/1211) - chore(deps): Bump github.com/stretchr/testify from 1.8.1 to 1.8.2 -* [#1203](https://github.com/NibiruChain/nibiru/pull/1203) - ci: make chaosnet pull nibiru image if --build is not specified -* [#1197](https://github.com/NibiruChain/nibiru/pull/1197) - feat: add fees into events in spot module. - * add `fees` field into `EventPoolCreated` event. - * add `fees` field into `EventPoolExited` event. - * add `fee` field into `EventAssetsSwapped` event. -* [#1222](https://github.com/NibiruChain/nibiru/pull/1222) - chore(deps): bump google.golang.org/protobuf from 1.28.2-0.20220831092852-f930b1dc76e8 to 1.29.0 -* [#1223](https://github.com/NibiruChain/nibiru/pull/1223) - chore(deps): bump github.com/golang/protobuf from 1.5.2 to 1.5.3 -* [#1205](https://github.com/NibiruChain/nibiru/pull/1205) - test: first testing framework skeleton and example -* [#1228](https://github.com/NibiruChain/nibiru/pull/1228) - feat: update github.com/CosmWasm/wasmd 0.29.2 -* [#1244](https://github.com/NibiruChain/nibiru/pull/1244) - feat: add typed event for oracle post price -* [#1237](https://github.com/NibiruChain/nibiru/pull/1237) - feat: reduce gas on openposition -* [#1231](https://github.com/NibiruChain/nibiru/pull/1231) - chore(deps): bump github.com/cosmos/ibc-go/v4 from 4.2.0 to 4.3.0 #1231 -* [#1256](https://github.com/NibiruChain/nibiru/pull/1256) - chore(deps): bump github.com/spf13/cobra from 1.6.1 to 1.7.0 -* [#1289](https://github.com/NibiruChain/nibiru/pull/1289) - feat: SqrtDepth equal to base reserves when pool creation -* [#1290](https://github.com/NibiruChain/nibiru/pull/1290) - refactor: fix quote/base reserve naming convention -* [#1311](https://github.com/NibiruChain/nibiru/pull/1311) - feat(perp): add PerpKeeperV2 -* [#1308](https://github.com/NibiruChain/nibiru/pull/1308) - feat(perp): ensure there's no int overflow in liq depth calculation -* [#1311](https://github.com/NibiruChain/nibiru/pull/1311) - feat(perp): add Calc and Twap methods -* [#1319](https://github.com/NibiruChain/nibiru/pull/1319) - test: add integration test actions -* [#1329](https://github.com/NibiruChain/nibiru/pull/1329) - feat(perp): add PerpKeeperV2 withdraw methods -* [#1328](https://github.com/NibiruChain/nibiru/pull/1328) - feat(perp): add PerpKeeperV2 swap methods -* [#1331](https://github.com/NibiruChain/nibiru/pull/1331) - refactor(perp): create perp v1 type package and module package -* [#1333](https://github.com/NibiruChain/nibiru/pull/1333) - feat(perp): add basic clearing house functions -* [#1332](https://github.com/NibiruChain/nibiru/pull/1332) - feat(perp): add hooks to update funding rate -* [#1334](https://github.com/NibiruChain/nibiru/pull/1334) - feat(perp): add PerpKeeperV2 `ClosePosition` -* [#1335](https://github.com/NibiruChain/nibiru/pull/1335) - refactor(perp): move remaining perpv1 files to v1 directory -* [#1338](https://github.com/NibiruChain/nibiru/pull/1338) - feat(perp): V2 OpenPosition -* [#1344](https://github.com/NibiruChain/nibiru/pull/1344) - feat(perp): PerpKeeperV2 `AddMargin` and `RemoveMargin` -* [#1345](https://github.com/NibiruChain/nibiru/pull/1345) - feat(perp): PerpV2 QueryServer -* [#1343](https://github.com/NibiruChain/nibiru/pull/1343) - feat(perp): add PerpKeeperV2 `MultiLiquidate` -* [#1352](https://github.com/NibiruChain/nibiru/pull/1352) - feat(perp): add PerpKeeperV2 `MsgServer` -* [#1350](https://github.com/NibiruChain/nibiru/pull/1350) - feat(perp): `EditPriceMultiplier` and `EditSwapInvariant` -* [#1341](https://github.com/NibiruChain/nibiru/pull/1341) - feat(bindings/oracle): add bindings for oracle module params -* [#1361](https://github.com/NibiruChain/nibiru/pull/1361) - feat(perp): add `PerpV2` module -* [#1363](https://github.com/NibiruChain/nibiru/pull/1363) - feat(perp): wire `PerpV2` module -* [#1365](https://github.com/NibiruChain/nibiru/pull/1365) - refactor(perp): split `perp` module into v1/ and v2/ -* [#1366](https://github.com/NibiruChain/nibiru/pull/1366) - feat: fix bindings test in cw_test -* [#1362](https://github.com/NibiruChain/nibiru/pull/1362) - feat(perp): add `perpv2` cli -* [#1369](https://github.com/NibiruChain/nibiru/pull/1369) - refactor(oracle): divert rewards from `perpv2` instead of `perpv1` -* [#1370](https://github.com/NibiruChain/nibiru/pull/1370) - feat(perp): `perpv2` `CreatePool` method -* [#1371](https://github.com/NibiruChain/nibiru/pull/1371) - feat: realize bad debt when a user tries to close his position -* [#1373](https://github.com/NibiruChain/nibiru/pull/1373) - feat(perp): `perpv2` `add-genesis-perp-market` CLI command -* [#1381](https://github.com/NibiruChain/nibiru/pull/1381) - chore(deps): Bump github.com/cosmos/cosmos-sdk to 0.45.16 -* [#1405](https://github.com/NibiruChain/nibiru/pull/1405) - ci: use Buf to build protos -* [#1406](https://github.com/NibiruChain/nibiru/pull/1406) - feat(perp): emit additional event info -* [#1419](https://github.com/NibiruChain/nibiru/pull/1419) - fix(spot): add pools to genesis state -* [#1408](https://github.com/NibiruChain/nibiru/pull/1408) - feat(spot): idempotent events -* [#1420](https://github.com/NibiruChain/nibiru/pull/1420) - refactor(oracle): update default params -* [#1421](https://github.com/NibiruChain/nibiru/pull/1421) - feat(oracle): add expiry time to oracle prices -* [#1422](https://github.com/NibiruChain/nibiru/pull/1422) - fix(oracle): handle zero oracle rewards -* [#1426](https://github.com/NibiruChain/nibiru/pull/1426) - refactor(perp): remove price fluctuation limit check -* [#1423](https://github.com/NibiruChain/nibiru/pull/1423) - fix: remove panics from abci hooks -* [#1579](https://github.com/NibiruChain/nibiru/pull/1579) - chore(proto): Add a buf.gen.rs.yaml and corresponding script to create Rust types for Wasm Stargate messages +- [#1820](https://github.com/NibiruChain/nibiru/pull/1820) - feat: add interchain accounts ### Bug Fixes -* [#1459](https://github.com/NibiruChain/nibiru/pull/1459) - fix(spot): wire `x/spot` msgService into app router -* [#1467](https://github.com/NibiruChain/nibiru/pull/1467) - fix(oracle): make `calcTwap` safer -* [#1464](https://github.com/NibiruChain/nibiru/pull/1464) - fix(gov): wire legacy proposal handlers -* [#1586](https://github.com/NibiruChain/nibiru/pull/1586) - fix(sudo): make messages compatible with `Amino` -* [#1210](https://github.com/NibiruChain/nibiru/pull/1210) - fix(ci): fix docker push workflow -* [#1337](https://github.com/NibiruChain/nibiru/pull/1337) - fix(ci): fix dockerfile with rocksdb -* [#1379](https://github.com/NibiruChain/nibiru/pull/1379) - feat(perp): check for denom in add/remove margin -* [#1383](https://github.com/NibiruChain/nibiru/pull/1383) - feat: enforce contract to be whitelisted when calling perp bindings -* [#1397](https://github.com/NibiruChain/nibiru/pull/1397) - fix: ensure margin is high enough when removing it -* [#1417](https://github.com/NibiruChain/nibiru/pull/1417) - fix: run end blocker on block end for perp v2 -* [#1425](https://github.com/NibiruChain/nibiru/pull/1425) - fix: remove positions from state when closed with reverse position -* [#1441](https://github.com/NibiruChain/nibiru/pull/1441) - fix(oracle): ignore abstain votes in std dev calculation -* [#1446](https://github.com/NibiruChain/nibiru/pull/1446) - fix(cmd): Add custom InitCmd to set set desired Tendermint consensus params for each node. -* [#1452](https://github.com/NibiruChain/nibiru/pull/1452) - fix(oracle): continue with abci hook during error -* [#1451](https://github.com/NibiruChain/nibiru/pull/1451) - fix(perp): decrease position with zero size - -### State Machine Breaking - -* [#1473](https://github.com/NibiruChain/nibiru/pull/1473) - refactor(perp)!: rename `OpenPosition` to `MarketOrder` -* [#1477](https://github.com/NibiruChain/nibiru/pull/1477) - refactor(oracle)!: Move away from deprecated events to typed events in x/oracle - -### API Breaking - -* [#1380](https://github.com/NibiruChain/nibiru/pull/1380) - feat(wasm): Add CreateMarket admin call for the controller contract -* [#1359](https://github.com/NibiruChain/nibiru/pull/1359) - feat(perp): Add InsuranceFundWithdraw admin call with corresponding smart contract -* [#1356](https://github.com/NibiruChain/nibiru/pull/1356) - build: Regress wasmvm (v1.1.1), tendermint (v0.34.24), and Cosmos-SDK (v0.45.14) dependencies -* [#1346](https://github.com/NibiruChain/nibiru/pull/1346) - build: Upgrade wasmvm (v1.2.1), tendermint (v0.34.26), and Cosmos-SDK (v0.45.14) dependencies -* [#1317](https://github.com/NibiruChain/nibiru/pull/1317) - feat(sudo): Implement and test CLI commands for tx and queries. -* [#1307](https://github.com/NibiruChain/nibiru/pull/1307) - feat(sudo): Create the x/sudo module + integration tests -* [#1299](https://github.com/NibiruChain/nibiru/pull/1299) - feat(wasm): Add peg shift bindings -* [#1292](https://github.com/NibiruChain/nibiru/pull/1292) - feat(wasm): Add module bindings for execute calls in x/perp: OpenPosition, ClosePosition, AddMargin, RemoveMargin. -* [#1287](https://github.com/NibiruChain/nibiru/pull/1287) - feat(wasm): Add module bindings for custom queries in x/perp: Reserves, AllMarkets, BasePrice, PremiumFraction, Metrics, PerpParams, PerpModuleAccounts -* [#1282](https://github.com/NibiruChain/nibiru/pull/1282) - feat(inflation)!: add inflation module -* [#1270](https://github.com/NibiruChain/nibiru/pull/1270) - refactor(proto)!: lint protos and standardize versioning -* [#1271](https://github.com/NibiruChain/nibiru/pull/1271) - refactor(perp)!: vpool → perp/amm #2 | imports and renames -* [#1269](https://github.com/NibiruChain/nibiru/pull/1269) - refactor(perp)!: merge x/util with x/perp -* [#1267](https://github.com/NibiruChain/nibiru/pull/1267) - refactor(perp)!: vpool → perp/amm #1 | Moves types, keeper, and cli -* [#1243](https://github.com/NibiruChain/nibiru/pull/1243) - feat(vpool): sqrt of liquidity depth tracked on pool -* [#1220](https://github.com/NibiruChain/nibiru/pull/1220) - feat: reduce gas fees when posting price -* [#1229](https://github.com/NibiruChain/nibiru/pull/1229) - feat: upgrade ibc to v4.2.0 and wasm v0.30.0 -* [#1254](https://github.com/NibiruChain/nibiru/pull/1254) - feat: add bias field into vpool -* [#1255](https://github.com/NibiruChain/nibiru/pull/1255) - feat: add peg multiplier field into vpool, which for now defaults to 1 -* [#1281](https://github.com/NibiruChain/nibiru/pull/1281) - feat: add peg multiplier to the pricing logic -* [#1291](https://github.com/NibiruChain/nibiru/pull/1291) - refactor(perp)!: add perp v2 state protos -* [#1296](https://github.com/NibiruChain/nibiru/pull/1296) - refactor(perp)!: update perp v2 state protos -* [#1298](https://github.com/NibiruChain/nibiru/pull/1298) - refactor(perp)!: remove `MaxOracleSpreadRatio` from Perpv2 -* [#1302](https://github.com/NibiruChain/nibiru/pull/1302) - refactor(oracle)!: price snapshot start time inclusive -* [#1301](https://github.com/NibiruChain/nibiru/pull/1301) - fix(epochs)!: correct epoch start time -* [#1304](https://github.com/NibiruChain/nibiru/pull/1304) - feat: db backend - rocksdb -* [#1305](https://github.com/NibiruChain/nibiru/pull/1305) - refactor(perp!): Remove unnecessary protos -* [#1312](https://github.com/NibiruChain/nibiru/pull/1312) - feat(wasm): wire depth shift handler to the wasm router -* [#1306](https://github.com/NibiruChain/nibiru/pull/1306) - feat(perp): complete perp v2 types -* [#1309](https://github.com/NibiruChain/nibiru/pull/1309) - feat: minimum swap amount set to $1 -* [#1336](https://github.com/NibiruChain/nibiru/pull/1336) - feat: move oracle params out of params subspace and onto the keeper -* [#1315](https://github.com/NibiruChain/nibiru/pull/1315) - feat: oracle rewards distribution every week -* [#1342](https://github.com/NibiruChain/nibiru/pull/1342) - feat(perp): market not enabled can only be used to close out existing positions -* [#1367](https://github.com/NibiruChain/nibiru/pull/1367) - feat: wire enable market to wasm -* [#1382](https://github.com/NibiruChain/nibiru/pull/1382) - refactor(perp)!: remove `perpv1` -* [#1385](https://github.com/NibiruChain/nibiru/pull/1385) - test(perp): add clearing house negative tests -* [#1388](https://github.com/NibiruChain/nibiru/pull/1388) - refactor(perp)!: idempotent position changed event -* [#1387](https://github.com/NibiruChain/nibiru/pull/1387) - feat: upgrade to Cosmos SDK v0.46.10 -* [#1413](https://github.com/NibiruChain/nibiru/pull/1413) - fix(perp): provide descriptive errors when all liquidations fail in MultiLiquidate -* [#1427](https://github.com/NibiruChain/nibiru/pull/1427) - refactor(perp)!: PositionChangedEvent `MarginToUser` -* [#1407](https://github.com/NibiruChain/nibiru/pull/1407) - feat!: upgrade to Cosmos SDK v0.47.3 - -### Dependencies - -* Bump `robinraju/release-downloader` from 1.6 to 1.8 (#1326) -* Bump `pozetroninc/github-action-get-latest-release` from 0.6.0 to 0.7.0 (#1325) -* Bump `technote-space/get-diff-action` from 4 to 6 (#1327) -* Bump `actions/setup-go` from 3 to 4 (#1324) -* Bump `github.com/docker/distribution` from 2.8.1+incompatible to 2.8.2+incompatible (#1339) -* Bump `github.com/CosmWasm/wasmvm` from 1.2.1 to 1.4.0 (#1354, #1507, [#1564](https://github.com/NibiruChain/nibiru/pull/1564)) -* Bump `github.com/spf13/cast` from 1.5.0 to 1.5.1 (#1358) -* Bump `github.com/stretchr/testify` from 1.8.2 to 1.8.4 (#1384, #1435) -* Bump `cosmossdk.io/math` from 1.0.0-beta.6 to 1.1.2 (#1394, [#1547](https://github.com/NibiruChain/nibiru/pull/1547)) -* Bump `google.golang.org/grpc` from 1.53.0 to 1.58.2 (#1395, #1437, #1443, #1497, [#1525](https://github.com/NibiruChain/nibiru/pull/1525), [#1568](https://github.com/NibiruChain/nibiru/pull/1568), [#1582](https://github.com/NibiruChain/nibiru/pull/1582), [#1598](https://github.com/NibiruChain/nibiru/pull/1598)) -* Bump `github.com/gin-gonic/gin` from 1.8.1 to 1.9.1 (#1409) -* Bump `github.com/spf13/viper` from 1.15.0 to 1.16.0 (#1436) -* Bump `github.com/prometheus/client_golang` from 1.15.1 to 1.16.0 (#1431) -* Bump `github.com/cosmos/ibc-go/v7` from 7.1.0 to 7.3.0 (#1445, [#1562](https://github.com/NibiruChain/nibiru/pull/1562)) -* Bump `bufbuild/buf-setup-action` from 1.21.0 to 1.26.1 (#1449, #1469, #1505, #1510, [#1537](https://github.com/NibiruChain/nibiru/pull/1537), [#1540](https://github.com/NibiruChain/nibiru/pull/1540), [#1544](https://github.com/NibiruChain/nibiru/pull/1544)) -* Bump `google.golang.org/protobuf` from 1.30.0 to 1.31.0 (#1450) -* Bump `cosmossdk.io/errors` from 1.0.0-beta.7 to 1.0.0 (#1499) -* Bump `github.com/holiman/uint256` from 1.2.2 to 1.2.3 (#1504) -* Bump `docker/build-push-action` from 4 to 5 ([#1572](https://github.com/NibiruChain/nibiru/pull/1572)) -* Bump `docker/login-action` from 2 to 3 ([#1571](https://github.com/NibiruChain/nibiru/pull/1571)) -* Bump `docker/setup-buildx-action` from 2 to 3 ([#1570](https://github.com/NibiruChain/nibiru/pull/1570)) -* Bump `docker/setup-qemu-action` from 2 to 3 ([#1569](https://github.com/NibiruChain/nibiru/pull/1569)) -* Bump `github.com/cosmos/cosmos-sdk` from v0.47.4 to v0.47.5 ([#1578](https://github.com/NibiruChain/nibiru/pull/1578)) -* Bump `codecov/codecov-action` from 3 to 4 ([#1583](https://github.com/NibiruChain/nibiru/pull/1583)) -* Bump `actions/checkout` from 3 to 4 ([#1593](https://github.com/NibiruChain/nibiru/pull/1593)) - -## [v0.19.2](https://github.com/NibiruChain/nibiru/releases/tag/v0.19.2) - 2023-02-24 - -### Features - -* [#1187](https://github.com/NibiruChain/nibiru/pull/1187) - feat(oracle): default vote threshold and min voters -* [#1276](https://github.com/NibiruChain/nibiru/pull/1276) - feat: add ewma function -* [#1284](https://github.com/NibiruChain/nibiru/pull/1284) - feat: fails if base and quote reserves are not equal on CreatePool -* [#1286](https://github.com/NibiruChain/nibiru/pull/1286) - feat: bias is zero when creating pool - -### API Breaking - -* [#1196](https://github.com/NibiruChain/nibiru/pull/1196) - refactor(spot)!: default whitelisted asset and query cli -* [#1195](https://github.com/NibiruChain/nibiru/pull/1195) - feat(perp)!: Add `MultiLiquidation` feature for perps -* [#1158](https://github.com/NibiruChain/nibiru/pull/1158) - feat(asset-registry)!: Add `AssetRegistry` -* [#1171](https://github.com/NibiruChain/nibiru/pull/1171) - refactor(asset)!: Replace `common.AssetPair` with `asset.Pair`. -* [#1164](https://github.com/NibiruChain/nibiru/pull/1164) - refactor: remove client interface for liquidate msg -* [#1173](https://github.com/NibiruChain/nibiru/pull/1173) - refactor(spot)!: replace `x/dex` module with `x/spot`. -* [#1176](https://github.com/NibiruChain/nibiru/pull/1176) - refactor(spot)!: replace `x/dex` module with `x/spot`. - -### State Machine Breaking - -* [#xxx](https://github.com/NibiruChain/nibiru/pull/xxx) - fix(wasm)!: call `ValidateBasic` before all `sdk.Msg` calls for the bindings-perp contract + remove sudo permissioning -* [#1154](https://github.com/NibiruChain/nibiru/pull/1154) - refactor(asset-pair)!: refactors `common.AssetPair` as an extension of string -* [#1156](https://github.com/NibiruChain/nibiru/pull/1156) - refactor: remove lockup & incentivation module +- [#1864](https://github.com/NibiruChain/nibiru/pull/1864) - fix(ica): add ICA controller stack ### Improvements -* [#1197](https://github.com/NibiruChain/nibiru/pull/1197) - refactor(testutil): clean up `x/common/testutil` test setup code -* [#1193](https://github.com/NibiruChain/nibiru/pull/1193) - refactor(oracle): clean up `x/oracle/keeper` tests -* [#1192](https://github.com/NibiruChain/nibiru/pull/1192) - feat: chaosnet docker-compose -* [#1191](https://github.com/NibiruChain/nibiru/pull/1191) - fix(oracle): default whitelisted pairs -* [#1189](https://github.com/NibiruChain/nibiru/pull/1189) - ci(codecov): add Codecov reporting -* [#1184](https://github.com/NibiruChain/nibiru/pull/1184) - docs(oracle): proto type docs, (2) spec clean-up, and (3) remove panic case -* [#1181](https://github.com/NibiruChain/nibiru/pull/1181) - refactor(oracle): keeper method locations -* [#1180](https://github.com/NibiruChain/nibiru/pull/1180) - refactor(oracle): whitelist refactor -* [#1179](https://github.com/NibiruChain/nibiru/pull/1179) - refactor(oracle): types refactor for validator performance map and whitelist map -* [#1161](https://github.com/NibiruChain/nibiru/pull/1161) - refactor: migrate simapp tests to use main app -* [#1134](https://github.com/NibiruChain/nibiru/pull/1134) - refactor: remove panics from vpool and spillovers from the perp module. It's now impossible to call functions in x/perp that would panic in vpool. -* [#1127](https://github.com/NibiruChain/nibiru/pull/1127) - refactor: remove unnecessary panics from x/dex and x/stablecoin -* [#1126](https://github.com/NibiruChain/nibiru/pull/1126) - refactor(perp): remove unnecessary panics -* [#1138](https://github.com/NibiruChain/nibiru/pull/1138) - refactor: put Makefile workflows in separate directory -* [#1126](https://github.com/NibiruChain/nibiru/pull/1126) - test(oracle): stop the tyrannical behavior of TestFuzz_PickReferencePair -* [#1135](https://github.com/NibiruChain/nibiru/pull/1135) - fix: add genesis oracle prices to localnet -* [#1141](https://github.com/NibiruChain/nibiru/pull/1141) - refactor(oracle): rename variables for readability -* [#1146](https://github.com/NibiruChain/nibiru/pull/1146) - fix: local docker-compose network -* [#1145](https://github.com/NibiruChain/nibiru/pull/1145) - chore: add USD quote asset -* [#1160](https://github.com/NibiruChain/nibiru/pull/1160) - feat: generic set -* [#1139](https://github.com/NibiruChain/nibiru/pull/1139) - feat: add default oracle whitelisted pairs -* [#1032](https://github.com/NibiruChain/nibiru/pull/1107) - ci: Create e2e wasm contract test -* [#1144](https://github.com/NibiruChain/nibiru/pull/1144) - ci: release for linux and darwin (arm64 and amd64) -* [#1165](https://github.com/NibiruChain/nibiru/pull/1165) - chore(deps): bump cosmos-sdk to [v0.45.12](https://github.com/cosmos/cosmos-sdk/blob/release/v0.45.x/CHANGELOG.md#v04512---2023-01-23) -* [#1149](https://github.com/NibiruChain/nibiru/pull/1149) - chore(deps): Bump [github.com/btcsuite/btcd](https://github.com/btcsuite/btcd) from 0.22.1 to 0.22.2 -* [#1089](https://github.com/NibiruChain/nibiru/pull/1089) - refactor(deps): Bump [github.com/holiman/uint256](https://github.com/holiman/uint256) from 1.1.1 to 1.2.1 (syntax changes) -* [#1188](https://github.com/NibiruChain/nibiru/pull/1188) - fix(spot): remove A precision and clean up borked logic -* [#1190](https://github.com/NibiruChain/nibiru/pull/1190) - ci(release): fix TM_VERSION not being set on releases -* [#1218](https://github.com/NibiruChain/nibiru/pull/1218) - ci(release): Publish chaosnet image when tagging a release -* [#1283](https://github.com/NibiruChain/nibiru/pull/1283) - chore(deps): bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0 - -### Bug Fixes - -* [#1194](https://github.com/NibiruChain/nibiru/pull/1194) - fix(oracle): local min voters -* [#1126](https://github.com/NibiruChain/nibiru/pull/1126) - test(oracle): stop the tyrannical behavior of TestFuzz_PickReferencePair -* [#1131](https://github.com/NibiruChain/nibiru/pull/1131) - fix(oracle): use correct distribution module account -* [#1151](https://github.com/NibiruChain/nibiru/pull/1151) - fix(dex): fix swap calculation for stableswap pools -* [#1210](https://github.com/NibiruChain/nibiru/pull/1210) - fix(ci): fix docker push workflow -* [#1212](https://github.com/NibiruChain/nibiru/pull/1212) - fix(spot): gracefully handle join spot pool with wrong tokens denom -* [#1219](https://github.com/NibiruChain/nibiru/pull/1219) - fix(ci): use chaosnet image on chaosnet docker compose -* [#1414](https://github.com/NibiruChain/nibiru/pull/1414) - fix(oracle): Add deterministic map iterations to avoid consensus failure. - -## [v0.16.3](https://github.com/NibiruChain/nibiru/releases/tag/v0.16.3) - -### Features - -* [#1115](https://github.com/NibiruChain/nibiru/pull/1115) - feat: improve single asset join calculation -* [#1117](https://github.com/NibiruChain/nibiru/pull/1117) - feat: wire multi-liquidate transaction -* [#1120](https://github.com/NibiruChain/nibiru/pull/1120) - feat: replace pricefeed with oracle - -### Bug Fixes - -* [#1113](https://github.com/NibiruChain/nibiru/pull/1113) - fix: fix quick simulation issue -* [#1114](https://github.com/NibiruChain/nibiru/pull/1114) - fix(dex): fix single asset join -* [#1116](https://github.com/NibiruChain/nibiru/pull/1116) - fix(dex): unfroze pool when LP share supply of 0 -* [#1124](https://github.com/NibiruChain/nibiru/pull/1124) - fix(dex): fix unexpected panic in stableswap calcs - -## [v0.16.2](https://github.com/NibiruChain/nibiru/releases/tag/v0.16.2) - Dec 13, 2022 - -### Features - -* [#1032](https://github.com/NibiruChain/nibiru/pull/1032) - feeder: add price provide API and bitfinex price source -* [#1038](https://github.com/NibiruChain/nibiru/pull/1038) - feat(dex): add single asset join -* [#1050](https://github.com/NibiruChain/nibiru/pull/1050) - feat(dex): add stableswap pools -* [#1058](https://github.com/NibiruChain/nibiru/pull/1058) - feature: use collections external lib -* [#1082](https://github.com/NibiruChain/nibiru/pull/1082) - feat(vpool): Add gov proposal for editing the sswap invariant of a vpool.. -* [#1092](https://github.com/NibiruChain/nibiru/pull/1092) - refactor(dex)!: revive dex module using intermediate test app -* [#1097](https://github.com/NibiruChain/nibiru/pull/1097) - feat(perp): Track and expose the net size of a pair with a query -* [#1105](https://github.com/NibiruChain/nibiru/pull/1105) - feat(perp): Add (notional) volume to metrics state - -### API Breaking - -* [#1074](https://github.com/NibiruChain/nibiru/pull/1074) - feat(vpool): Add gov proposal for editing the vpool config without changing the reserves. - -### State Machine Breaking - -* [#1102](https://github.com/NibiruChain/nibiru/pull/1102) - refactor(perp)!: replace CumulativePremiumFractions array with single value - -### Breaking Changes - -* [#1074](https://github.com/NibiruChain/nibiru/pull/1074) - feat(vpool): Add gov proposal for editing the vpool config without changing the reserves. - -### Improvements - -* [#1111](https://github.com/NibiruChain/nibiru/pull/1111) - feat(vpool)!: Use flags and certain default values instead of unnamed args for add-genesis-vpool to improve ease of use -* [#1046](https://github.com/NibiruChain/nibiru/pull/1046) - remove: feeder. The price feeder was moved to an external repo. -* [#1015](https://github.com/NibiruChain/nibiru/pull/1015) - feat(dex): throw error when swap output amount is less than 1 -* [#1018](https://github.com/NibiruChain/nibiru/pull/1018) - chore(dex): refactor to match best practice -* [#1024](https://github.com/NibiruChain/nibiru/pull/1024) - refactor(oracle): remove Pair and PairList -* [#1034](https://github.com/NibiruChain/nibiru/pull/1034) - refactor(proto): use proto-typed events x/dex -* [#1035](https://github.com/NibiruChain/nibiru/pull/1035) - refactor(proto): use proto-typed events for epochs -* [#1014](https://github.com/NibiruChain/nibiru/pull/1014) - refactor(oracle): full refactor of EndBlock UpdateExchangeRates() long function -* [#1054](https://github.com/NibiruChain/nibiru/pull/1054) - chore(deps): Bump github.com/cosmos/ibc-go/v3 from 3.3.0 to 3.4.0 -* [#1043](https://github.com/NibiruChain/nibiru/pull/1043) - chore(deps): Bump github.com/spf13/cobra from 1.6.0 to 1.6.1 -* [#1056](https://github.com/NibiruChain/nibiru/pull/1056) - chore(deps): Bump github.com/prometheus/client_golang from 1.13.0 to 1.13.1 -* [#1055](https://github.com/NibiruChain/nibiru/pull/1055) - chore(deps): Bump github.com/spf13/viper from 1.13.0 to 1.14.0 -* [#1061](https://github.com/NibiruChain/nibiru/pull/1061) - feat(cmd): hard-code block time parameters in the Tendermint config -* [#1068](https://github.com/NibiruChain/nibiru/pull/1068) - refactor(vpool)!: Remove ReserveSnapshot from the vpool genesis state since reserves are taken automatically on vpool initialization. -* [#1064](https://github.com/NibiruChain/nibiru/pull/1064) - test(wasm): add test for Cosmwasm -* [#1075](https://github.com/NibiruChain/nibiru/pull/1075) - feat(dex): remove possibility to create multiple pools with the same assets -* [#1080](https://github.com/NibiruChain/nibiru/pull/1080) - feat(perp): Add exchanged notional to the position changed event #1080 -* [#1082](https://github.com/NibiruChain/nibiru/pull/1082) - feat(localnet.sh): Set genesis prices based on real BTC and ETH prices -* [#1086](https://github.com/NibiruChain/nibiru/pull/1086) - refactor(perp)!: Removed unused field, `LiquidationPenalty`, from `PositionChangedEvent` -* [#1093](https://github.com/NibiruChain/nibiru/pull/1093) - simulation(dex): add simulation tests for stableswap pools -* [#1091](https://github.com/NibiruChain/nibiru/pull/1091) - refactor: Use common.Precision instead of 1_000_000 in the codebase -* [#1109](https://github.com/NibiruChain/nibiru/pull/1109) - refactor(vpool)!: Condense swap SwapXForY and SwapYForX events into SwapEvent - -### Bug Fixes - -* [#1100](https://github.com/NibiruChain/nibiru/pull/1100) - fix(oracle): fix flaky oracle test -* [#1110](https://github.com/NibiruChain/nibiru/pull/1110) - fix(dex): fix dex issue on unsorted join pool - -### CI - -* [#1088](https://github.com/NibiruChain/nibiru/pull/1088) - ci: build cross binaries - -## v0.15.0 - -### CI - -* [#785](https://github.com/NibiruChain/nibiru/pull/785) - ci: create simulations job - -### State Machine Breaking - -* [#994](https://github.com/NibiruChain/nibiru/pull/994) - x/oracle refactor to use collections -* [#991](https://github.com/NibiruChain/nibiru/pull/991) - collections refactoring of keys and values -* [#978](https://github.com/NibiruChain/nibiru/pull/978) - x/vpool move state logic to collections -* [#977](https://github.com/NibiruChain/nibiru/pull/977) - x/perp add whitelisted liquidators -* [#960](https://github.com/NibiruChain/nibiru/pull/960) - x/common validate asset pair denoms -* [#952](https://github.com/NibiruChain/nibiru/pull/952) - x/perp move state logic to collections -* [#872](https://github.com/NibiruChain/nibiru/pull/872) - x/perp remove module balances from genesis -* [#878](https://github.com/NibiruChain/nibiru/pull/878) - rename `PremiumFraction` to `FundingRate` -* [#900](https://github.com/NibiruChain/nibiru/pull/900) - refactor x/vpool snapshot state management -* [#904](https://github.com/NibiruChain/nibiru/pull/904) - refactor: change Pool name to VPool in vpool module -* [#894](https://github.com/NibiruChain/nibiru/pull/894) - add the collections package! -* [#897](https://github.com/NibiruChain/nibiru/pull/897) - x/pricefeed - use collections. -* [#933](https://github.com/NibiruChain/nibiru/pull/933) - refactor(perp): remove whitelist and simplify state keys -* [#959](https://github.com/NibiruChain/nibiru/pull/959) - feat(vpool): complete genesis import export - * removed Params from genesis. - * added pair into ReserveSnapshot type. - * added validation of snapshots and snapshots in genesis. -* [#975](https://github.com/NibiruChain/nibiru/pull/975) - fix(perp): funding payment calculations -* [#976](https://github.com/NibiruChain/nibiru/pull/976) - refactor(epochs): refactor to increase readability and some tests - * EpochInfo.CurrentEpoch changed from int64 to uint64. - -### API Breaking - -* [#880](https://github.com/NibiruChain/nibiru/pull/880) - refactor `PostRawPrice` return values -* [#900](https://github.com/NibiruChain/nibiru/pull/900) - fix x/vpool twap calculation to be bounded in time -* [#919](https://github.com/NibiruChain/nibiru/pull/919) - refactor(proto): vpool module files consistency - * MarkPriceChanged renamed to MarkPriceChangedEvent -* [#875](https://github.com/NibiruChain/nibiru/pull/875) - x/perp add MsgMultiLiquidate -* [#979](https://github.com/NibiruChain/nibiru/pull/979) - refactor and clean VPool. - -### Improvements - -* [#1044](https://github.com/NibiruChain/nibiru/pull/1044) - feat(wasm): cosmwasm module integration -* [#858](https://github.com/NibiruChain/nibiru/pull/858) - fix trading limit ratio check; checks in both directions on both quote and base assets -* [#865](https://github.com/NibiruChain/nibiru/pull/865) - refactor(vpool): clean up interface for CmdGetBaseAssetPrice to use add and remove as directions -* [#868](https://github.com/NibiruChain/nibiru/pull/868) - refactor dex integration tests to be independent between them -* [#876](https://github.com/NibiruChain/nibiru/pull/876) - chore(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0 -* [#879](https://github.com/NibiruChain/nibiru/pull/879) - test(perp): liquidate cli test and genesis fix for testutil initGenFiles -* [#889](https://github.com/NibiruChain/nibiru/pull/889) - feat: decouple keeper from servers in pricefeed module -* [#886](https://github.com/NibiruChain/nibiru/pull/886) - feat: decouple keeper from servers in perp module -* [#901](https://github.com/NibiruChain/nibiru/pull/901) - refactor(vpool): remove `GetUnderlyingPrice` method -* [#902](https://github.com/NibiruChain/nibiru/pull/902) - refactor(common): improve usability of `common.AssetPair` -* [#913](https://github.com/NibiruChain/nibiru/pull/913) - chore(epochs): update x/epochs module -* [#911](https://github.com/NibiruChain/nibiru/pull/911) - test(perp): add `MsgOpenPosition` simulation tests -* [#917](https://github.com/NibiruChain/nibiru/pull/917) - refactor(proto): perp module files consistency -* [#920](https://github.com/NibiruChain/nibiru/pull/920) - refactor(proto): pricefeed module files consistency -* [#926](https://github.com/NibiruChain/nibiru/pull/926) - feat: use spot twap for funding rate calculation -* [#932](https://github.com/NibiruChain/nibiru/pull/932) - refactor(perp): rename premium fraction to funding rate -* [#963](https://github.com/NibiruChain/nibiru/pull/963) - test: add collections api tests -* [#971](https://github.com/NibiruChain/nibiru/pull/971) - chore: use upstream 99designs/keyring module -* [#964](https://github.com/NibiruChain/nibiru/pull/964) - test(vpool): refactor flaky vpool cli test -* [#956](https://github.com/NibiruChain/nibiru/pull/956) - test(perp): partial liquidate unit test -* [#981](https://github.com/NibiruChain/nibiru/pull/981) - chore(testutil): clean up x/testutil packages -* [#980](https://github.com/NibiruChain/nibiru/pull/980) - test(perp): add `MsgClosePosition`, `MsgAddMargin`, and `MsgRemoveMargin` simulation tests -* [#987](https://github.com/NibiruChain/nibiru/pull/987) - feat: create a query that directly returns all module accounts without pagination or iteration -* [#982](https://github.com/NibiruChain/nibiru/pull/982) - improvements for pricefeed genesis -* [#989](https://github.com/NibiruChain/nibiru/pull/989) - test(perp): cli test for AddMargin -* [#1001](https://github.com/NibiruChain/nibiru/pull/1001) - chore(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0 -* [#1013](https://github.com/NibiruChain/nibiru/pull/1013) - test(vpool): more calc twap tests and documentation -* [#1012](https://github.com/NibiruChain/nibiru/pull/1012) - test(vpool): make vpool simulation with random parameters - -### Features - -* [#1019](https://github.com/NibiruChain/nibiru/pull/1019) - add fields to the snapshot reserve event -* [#1010](https://github.com/NibiruChain/nibiru/pull/1010) - feeder: initialize oracle feeder core logic -* [#966](https://github.com/NibiruChain/nibiru/pull/966) - collections: add indexed map -* [#852](https://github.com/NibiruChain/nibiru/pull/852) - feat(genesis): add cli command to add pairs at genesis -* [#861](https://github.com/NibiruChain/nibiru/pull/861) - feat: query cumulative funding payments -* [#985](https://github.com/NibiruChain/nibiru/pull/985) - feat: query all active positions for a trader -* [#997](https://github.com/NibiruChain/nibiru/pull/997) - feat: emit `ReserveSnapshotSavedEvent` in vpool EndBlocker -* [#1011](https://github.com/NibiruChain/nibiru/pull/1011) - feat(perp): add DonateToEF cli command -* [#1044](https://github.com/NibiruChain/nibiru/pull/1044) - feat(wasm): cosmwasm module integration - -### Fixes - -* [#1023](https://github.com/NibiruChain/nibiru/pull/1023) - collections: golang compiler bug -* [#1017](https://github.com/NibiruChain/nibiru/pull/1017) - collections: correctly reports value type and key in case of not found errors. -* [#857](https://github.com/NibiruChain/nibiru/pull/857) - x/perp add proper stateless genesis validation checks -* [#874](https://github.com/NibiruChain/nibiru/pull/874) - fix --home issue with unsafe-reset-all command, updating tendermint to v0.34.21 -* [#892](https://github.com/NibiruChain/nibiru/pull/892) - chore: fix localnet script -* [#925](https://github.com/NibiruChain/nibiru/pull/925) - fix(vpool): snapshot iteration -* [#930](https://github.com/NibiruChain/nibiru/pull/930) - fix(vpool): snapshot iteration on mark twap -* [#911](https://github.com/NibiruChain/nibiru/pull/911) - fix(perp): handle issue where no vpool snapshots are found -* [#958](https://github.com/NibiruChain/nibiru/pull/930) - fix(pricefeed): add twap to prices query -* [#961](https://github.com/NibiruChain/nibiru/pull/961) - fix(perp): wire the funding rate query -* [#993](https://github.com/NibiruChain/nibiru/pull/993) - fix(vpool): fluctuation limit check -* [#1000](https://github.com/NibiruChain/nibiru/pull/1000) - chore: bump cosmos-sdk to v0.45.9 to fix ibc bug -* [#1002](https://github.com/NibiruChain/nibiru/pull/1002) - fix: update go.mod dependencies to fix the protocgen script - -## v0.14.0 - -### API Breaking - -* [#830](https://github.com/NibiruChain/nibiru/pull/830) - test(vpool): Make missing fields for 'query vpool all-pools' display as empty strings. - * Improve test coverage of functions used in the query server. - * Added 'pair' field to the `all-pools` to make the prices array easier to digest -* [#878](https://github.com/NibiruChain/nibiru/pull/878) - rename `funding-payments` query to `funding-rate` - -### Improvements - -* [#837](https://github.com/NibiruChain/nibiru/pull/837) - simplify makefile, removing unused module creation and usage of new command to add vpool at genesis -* [#836](https://github.com/NibiruChain/nibiru/pull/836) - refactor(genesis): DRY improvements and functions added to localnet.sh for readability -* [#842](https://github.com/NibiruChain/nibiru/pull/842) - use self-hosted runner -* [#843](https://github.com/NibiruChain/nibiru/pull/843) - add timeout to github actions integration tests -* [#847](https://github.com/NibiruChain/nibiru/pull/847) - add command in localnet to whitelist oracle -* [#848](https://github.com/NibiruChain/nibiru/pull/848) - add check max leverage on add vpool in genesis command - -### Fixes - -* [#850](https://github.com/NibiruChain/nibiru/pull/850) - x/vpool - properly validate vpools at genesis -* [#854](https://github.com/NibiruChain/nibiru/pull/854) - add buildx to the docker release workflow - -### Features - -* [#827](https://github.com/NibiruChain/nibiru/pull/827) - feat(genesis): add cli command to add vpool at genesis -* [#838](https://github.com/NibiruChain/nibiru/pull/838) - feat(genesis): add cli command to whitelist oracles at genesis -* [#846](https://github.com/NibiruChain/nibiru/pull/846) - x/oracle remove reference pair - -## [v0.13.0](https://github.com/NibiruChain/nibiru/releases/tag/v0.13.0) - 2022-08-16 - -## API Breaking - -* [#831](https://github.com/NibiruChain/nibiru/pull/831) - remove modules that are not used in testnet - -### CI - -* [#795](https://github.com/NibiruChain/nibiru/pull/795) - integration tests run when PR is approved -* [#826](https://github.com/NibiruChain/nibiru/pull/826) - create and push docker image on release - -### Improvements - -* [#798](https://github.com/NibiruChain/nibiru/pull/798) - fix integration tests caused by PR #786 -* [#801](https://github.com/NibiruChain/nibiru/pull/801) - remove unused pair constants -* [#788](https://github.com/NibiruChain/nibiru/pull/788) - add --overwrite flag to the nibid init call of localnet.sh -* [#804](https://github.com/NibiruChain/nibiru/pull/804) - bump ibc-go to v3.1.1 -* [#817](https://github.com/NibiruChain/nibiru/pull/817) - Make post prices transactions gasless for whitelisted oracles -* [#818](https://github.com/NibiruChain/nibiru/pull/818) - fix(localnet.sh): add max leverage to vpools in genesis to fix open-position -* [#819](https://github.com/NibiruChain/nibiru/pull/819) - add golangci-linter using docker in Makefile -* [#835](https://github.com/NibiruChain/nibiru/pull/835) - x/oracle cleanup code - -### Features - -* [#839](https://github.com/NibiruChain/nibiru/pull/839) - x/oracle rewarding -* [#791](https://github.com/NibiruChain/nibiru/pull/791) Add the x/oracle module -* [#811](https://github.com/NibiruChain/nibiru/pull/811) Return the index twap in `QueryPrice` cmd -* [#813](https://github.com/NibiruChain/nibiru/pull/813) - (vpool): Expose mark price, mark TWAP, index price, and k (swap invariant) in the all-pools query -* [#816](https://github.com/NibiruChain/nibiru/pull/816) - Remove tobin tax from x/oracle -* [#810](https://github.com/NibiruChain/nibiru/pull/810) - feat(x/perp): expose 'marginRatioIndex' and block number on QueryPosition -* [#832](https://github.com/NibiruChain/nibiru/pull/832) - x/oracle app wiring - -### Documentation - -* [#814](https://github.com/NibiruChain/nibiru/pull/814) - docs(perp): Added events specification for the perp module. - -## [v0.12.1](https://github.com/NibiruChain/nibiru/releases/tag/v0.12.1) - 2022-08-04 - -* [#796](https://github.com/NibiruChain/nibiru/pull/796) - fix bug that caused that epochKeeper was nil when running epoch hook from Perp module -* [#793](https://github.com/NibiruChain/nibiru/pull/793) - add a vpool parameter to limit leverage in open position - -## [v0.12.0](https://github.com/NibiruChain/nibiru/releases/tag/v0.12.0) - 2022-08-03 - -### Improvements - -* [#775](https://github.com/NibiruChain/nibiru/pull/775) - bump google.golang.org/protobuf from 1.28.0 to 1.28.1 -* [#768](https://github.com/NibiruChain/nibiru/pull/768) - add simulation tests to make file -* [#767](https://github.com/NibiruChain/nibiru/pull/767) - add fluctuation limit checks on `OpenPosition`. -* [#786](https://github.com/NibiruChain/nibiru/pull/786) - add genesis params in localnet script. -* [#770](https://github.com/NibiruChain/nibiru/pull/770) - Return err in case of zero time elapsed and zero snapshots on `GetCurrentTWAP` func. If zero time has elapsed, and snapshots exists, return the instantaneous average. - -### Bug Fixes - -* [#766](https://github.com/NibiruChain/nibiru/pull/766) - Fixed margin ratio calculation for trader position. -* [#776](https://github.com/NibiruChain/nibiru/pull/776) - Fix a bug where the user could open infinite leverage positions -* [#779](https://github.com/NibiruChain/nibiru/pull/779) - Fix issue with released tokens being invalid in `ExitPool` - -### Testing - -* [#782](https://github.com/NibiruChain/nibiru/pull/782) - replace GitHub test workflows to use make commands -* [#784](https://github.com/NibiruChain/nibiru/pull/784) - fix runsim -* [#783](https://github.com/NibiruChain/nibiru/pull/783) - sanitise inputs for msg swap simulations - -## [v0.11.0](https://github.com/NibiruChain/nibiru/releases/tag/v0.11.0) - 2022-07-29 - -### Documentation - -* [#701](https://github.com/NibiruChain/nibiru/pull/701) Add release process guide - -### Improvements - -* [#715](https://github.com/NibiruChain/nibiru/pull/715) - remove redundant perp.Keeper.SetPosition parameters -* [#718](https://github.com/NibiruChain/nibiru/pull/718) - add guard clauses on OpenPosition (leverage and quote amount != 0) -* [#728](https://github.com/NibiruChain/nibiru/pull/728) - add dependabot file into the project. -* [#723](https://github.com/NibiruChain/nibiru/pull/723) - refactor perp keeper's `RemoveMargin` method -* [#730](https://github.com/NibiruChain/nibiru/pull/730) - update localnet script. -* [#736](https://github.com/NibiruChain/nibiru/pull/736) - Bumps [github.com/spf13/cast](https://github.com/spf13/cast) from 1.4.1 to 1.5.0 -* [#735](https://github.com/NibiruChain/nibiru/pull/735) - Bump github.com/spf13/cobra from 1.4.0 to 1.5.0 -* [#729](https://github.com/NibiruChain/nibiru/pull/729) - move maintenance margin to the vpool module -* [#741](https://github.com/NibiruChain/nibiru/pull/741) - remove unused code and refactored variable names. -* [#742](https://github.com/NibiruChain/nibiru/pull/742) - Vpools are not tradeable if they have invalid oracle prices. -* [#739](https://github.com/NibiruChain/nibiru/pull/739) - Bump github.com/spf13/viper from 1.11.0 to 1.12.0 - -### API Breaking - -* [#721](https://github.com/NibiruChain/nibiru/pull/721) - Updated proto property names to adhere to standard snake_casing and added Unlock REST endpoint -* [#724](https://github.com/NibiruChain/nibiru/pull/724) - Add position fields in `ClosePositionResponse`. -* [#737](https://github.com/NibiruChain/nibiru/pull/737) - Renamed from property to avoid python name clash - -### State Machine Breaking - -* [#733](https://github.com/NibiruChain/nibiru/pull/733) - Bump github.com/cosmos/ibc-go/v3 from 3.0.0 to 3.1.0 -* [#741](https://github.com/NibiruChain/nibiru/pull/741) - Rename `epoch_identifier` param to `funding_rate_interval`. -* [#745](https://github.com/NibiruChain/nibiru/pull/745) - Updated pricefeed twap calc to use bounded time - -### Bug Fixes - -* [#746](https://github.com/NibiruChain/nibiru/pull/746) - Pin cosmos-sdk version to v0.45 for proto generation. - -## [v0.10.0](https://github.com/NibiruChain/nibiru/releases/tag/v0.10.0) - 2022-07-18 - -### Improvements - -* [#705](https://github.com/NibiruChain/nibiru/pull/705) Refactor PerpKeeper's `AddMargin` method to accept individual fields instead of the entire Msg object. - -### API Breaking - -* [#709](https://github.com/NibiruChain/nibiru/pull/709) Add fields to `OpenPosition` response. -* [#707](https://github.com/NibiruChain/nibiru/pull/707) Add fluctuation limit checks in vpool methods. -* [#712](https://github.com/NibiruChain/nibiru/pull/712) Add funding rate calculation and `FundingRateChangedEvent`. - -### Upgrades - -* [#725](https://github.com/NibiruChain/nibiru/pull/725) Add governance handler for creating new virtual pools. -* [#702](https://github.com/NibiruChain/nibiru/pull/702) Add upgrade handler for v0.10.0. - -## [v0.9.2](https://github.com/NibiruChain/nibiru/releases/tag/v0.9.2) - 2022-07-11 - -### Improvements - -* [#686](https://github.com/NibiruChain/nibiru/pull/686) Add changelog enforcer to github actions. -* [#681](https://github.com/NibiruChain/nibiru/pull/681) Remove automatic release and leave integration tests when merge into master. -* [#684](https://github.com/NibiruChain/nibiru/pull/684) Reorganize PerpKeeper methods. -* [#690](https://github.com/NibiruChain/nibiru/pull/690) Call `closePositionEntirely` from `ClosePosition`. -* [#689](https://github.com/NibiruChain/nibiru/pull/689) Apply funding rate calculation 48 times per day. - -### API Breaking - -* [#687](https://github.com/NibiruChain/nibiru/pull/687) Emit `PositionChangedEvent` upon changing margin. -* [#685](https://github.com/NibiruChain/nibiru/pull/685) Represent `PositionChangedEvent` bad debt as Coin. -* [#697](https://github.com/NibiruChain/nibiru/pull/697) Rename pricefeed keeper methods. -* [#689](https://github.com/NibiruChain/nibiru/pull/689) Change liquidation params to 2.5% liquidation fee ratio and 25% partial liquidation ratio. +- [#1859](https://github.com/NibiruChain/nibiru/pull/1859) - refactor(oracle): add oracle slashing events -### Testing +--- -* [#695](https://github.com/NibiruChain/nibiru/pull/695) Add `OpenPosition` integration tests. -* [#692](https://github.com/NibiruChain/nibiru/pull/692) Add test coverage for Perp MsgServer methods. +[LEGACY CHANGELOG](./LEGACY-CHANGELOG.md) diff --git a/CHAOSNET.md b/CHAOSNET.md index f7cbd8e03..8a9e802fa 100644 --- a/CHAOSNET.md +++ b/CHAOSNET.md @@ -1,47 +1,375 @@ -# How to use +# Chaosnet -Before running +- [Chaosnet](#chaosnet) + - [What is Chaosnet?](#what-is-chaosnet) + - [How to run "chaosnet"](#how-to-run-chaosnet) + - [How to force pull images from the registry](#how-to-force-pull-images-from-the-registry) + - [IBC Commands](#ibc-commands) + - [Interchain Transfers](#interchain-transfers) + - [Interchain Accounts](#interchain-accounts) + - [Endpoints](#endpoints) + - [FAQ](#faq) + - [`make chaosnet` says that "Additional property name is not allowed"](#make-chaosnet-says-that-additional-property-name-is-not-allowed) + - [Does data persist between runs?](#does-data-persist-between-runs) + - [My `make chaosnet` takes forever to run](#my-make-chaosnet-takes-forever-to-run) -```sh -make chaosnet -``` +## What is Chaosnet? -you need to log into our private Docker image registry in order to pull the private images. Go to and generate a new token with `read:packages` scope. Copy the access token to your clipboard. +Chaosnet is an expanded version of localnet that runs: -Next, run +- up to two validators (nibiru-0 and nibiru-1) +- pricefeeders for each validator +- a hermes relayer between the two validators (if the `ibc` profile is used) +- a postgres:14 database (if the `heartmonitor` profile is used) +- a heartmonitor instance (if the `heartmonitor` profile is used) +- a graphql server (if the `heartmonitor` profile is used) -```sh +## How to run "chaosnet" + +1. Make sure you have [Docker](https://docs.docker.com/engine/install/) installed and running +2. Make sure you have `make` installed +3. (Optional) Docker login to ghcr.io (only if you want to use the `heartmonitor` profile) + +```bash docker login ghcr.io ``` - and enter your GitHub username for the `username` field, and your personal access token for the password. +Enter your GitHub username for the `username` field, and your personal access token for the password. -Now you can run +4. Run `make chaosnet` -```sh -make chaosnet -``` +Note that this will take a while the first time you run it, as it will need to pull all the images from the registry, build the chaonset image locally, and set up the IBC channel (which has a lot of round trip packet commits). + +Other profiles include + +- `make chaosnet-ibc`: creates two validator instances and a hermes relayer +- `make chaosnet-heartmonitor`: single validator with heartmonitor+graphql instance ## How to force pull images from the registry -By default, images won't re-fetch from upstream registries. To force a pull, you can run +By default, most images (heartmonitor, etc.) are cached locally and won't re-fetch from upstream registries. To force a pull, you can run ```sh make chaosnet-build ``` -to force re-build and pull images. +## IBC Commands + +### Interchain Transfers + +To send an IBC transfer from nibiru-0 to nibiru-1, run: + +1. SSH into nibiru-0 + + ```sh + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml exec -it nibiru-0 /bin/ash + ``` + +2. Transfer tokens from nibiru-0 to nibiru-1 + + ```sh + nibid tx ibc-transfer transfer transfer \ + channel-0 \ + nibi18mxturdh0mjw032c3zslgkw63cukkl4q5skk8g \ + 1000000unibi \ + --from validator \ + --fees 5000unibi \ + --yes | jq + ``` + +3. In a new shell, SSH into nibiru-1 + + ```sh + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml exec -it nibiru-1 /bin/ash + ``` + +4. Query the balance of nibiru-1 + + ```sh + # set the config since nibiru-1 has different ports + nibid config node "http://localhost:36657" + + nibid q bank balances $(nibid keys show validator -a) | jq + ``` + + Output: + + ```json + { + "balances": [ + { + "denom": "ibc/9BEE732637B12723D26E365D19CCB624587CE6254799EEE7C5F77B587BD677B0", + "amount": "1000000" + }, + { + "denom": "unibi", + "amount": "9999100000000" + } + ], + "pagination": { + "next_key": null, + "total": "0" + } + } + ``` + +5. Send tokens from nibiru-1 to nibiru-0 + + ```sh + nibid tx ibc-transfer transfer transfer \ + channel-0 \ + nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl \ + 5555unibi \ + --from validator \ + --fees 5000unibi \ + --yes | jq + ``` + +6. Go back to the nibiru-0 and query the balance + + ```sh + nibid q bank balances $(nibid keys show validator -a) | jq + ``` + + Output: + + ```json + { + "balances": [ + { + "denom": "ibc/9BEE732637B12723D26E365D19CCB624587CE6254799EEE7C5F77B587BD677B0", + "amount": "5555" + }, + { + "denom": "unibi", + "amount": "9999098995000" + } + ], + "pagination": { + "next_key": null, + "total": "0" + } + } + ``` + +7. Send IBC tokens back to nibiru-1 + + ```sh + nibid tx ibc-transfer transfer transfer \ + channel-0 \ + nibi18mxturdh0mjw032c3zslgkw63cukkl4q5skk8g \ + 5555ibc/9BEE732637B12723D26E365D19CCB624587CE6254799EEE7C5F77B587BD677B0 \ + --from validator \ + --fees 5000unibi \ + --yes | jq + ``` + +8. Verify tokens are sent + + ```sh + nibid q bank balances $(nibid keys show validator -a) | jq + ``` + + Output: + + ```json + { + "balances": [ + { + "denom": "unibi", + "amount": "9999098990000" + } + ], + "pagination": { + "next_key": null, + "total": "0" + } + } + ``` + +9. Back in the nibiru-1 shell, send tokens back to nibiru-0 + + ```sh + nibid tx ibc-transfer transfer transfer \ + channel-0 \ + nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl \ + 1000000ibc/9BEE732637B12723D26E365D19CCB624587CE6254799EEE7C5F77B587BD677B0 \ + --from validator \ + --fees 5000unibi \ + --yes | jq + ``` + +10. Verify tokens are sent + + ```sh + nibid q bank balances $(nibid keys show validator -a) | jq + ``` + + Output: + + ```json + { + "balances": [ + { + "denom": "unibi", + "amount": "9999099990000" + } + ], + "pagination": { + "next_key": null, + "total": "0" + } + } + ``` + +### Interchain Accounts + +The following steps assume nibiru-0 is the controller chain and nibiru-1 is the host chain. + +The goal is to have an nibiru-0 use an interchain account on nibiru-1 to stake to a validator. + +1. SSH into nibiru-0 + + ```sh + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml exec -it nibiru-0 /bin/ash + ``` + +2. Register an Interchain Account on nibiru-0 + + ```sh + # on nibiru-0 + + FROM=nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl + + nibid tx interchain-accounts controller \ + register \ + connection-0 \ + --from $FROM \ + --gas auto \ + --gas-adjustment 1.5 \ + --gas-prices 0.025unibi \ + --yes + ``` + +3. Query the interchain account address + + ```sh + # on nibiru-0 + + nibid q interchain-accounts controller \ + interchain-account \ + nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl \ + connection-0 | jq + + # nibi124zc9yjjksxrfrzpfvkysl3r8zrlef2rce5ccqt9mavgy66hhzmqtrvvlr + ``` + +4. In a new terminal, SSH into nibiru-1 + + ```sh + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml exec -it nibiru-1 /bin/ash + ``` + +5. Fund the interchain account + + ```sh + # on nibiru-1 + + nibid tx bank send \ + nibi18mxturdh0mjw032c3zslgkw63cukkl4q5skk8g \ + nibi124zc9yjjksxrfrzpfvkysl3r8zrlef2rce5ccqt9mavgy66hhzmqtrvvlr \ + 1000000unibi \ + --yes | jq + + nibid q bank balances nibi124zc9yjjksxrfrzpfvkysl3r8zrlef2rce5ccqt9mavgy66hhzmqtrvvlr | jq + ``` + +6. Generate packet data from the host chain + + ```sh + # on nibiru-1 + + cat << EOF | jq | tee msg_delegate.json + { + "@type":"/cosmos.staking.v1beta1.MsgDelegate", + "delegator_address":"nibi124zc9yjjksxrfrzpfvkysl3r8zrlef2rce5ccqt9mavgy66hhzmqtrvvlr", + "validator_address":"nibivaloper18mxturdh0mjw032c3zslgkw63cukkl4qatcdn4", + "amount": { + "denom": "unibi", + "amount": "500000" + } + } + EOF + + nibid tx interchain-accounts host \ + generate-packet-data \ + "$(cat msg_delegate.json)" \ + --encoding proto3 | jq + + # copy the output + ``` + +7. Send the packet from the controller chain + + ```sh + # on nibiru-0 + + cat << EOF | jq | tee packet.json + { + "type": "TYPE_EXECUTE_TX", + "data": "Cq4BCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRKGAQo/bmliaTEyNHpjOXlqamtzeHJmcnpwZnZreXNsM3I4enJsZWYycmNlNWNjcXQ5bWF2Z3k2Nmhoem1xdHJ2dmxyEjJuaWJpdmFsb3BlcjE4bXh0dXJkaDBtancwMzJjM3pzbGdrdzYzY3Vra2w0cWF0Y2RuNBoPCgV1bmliaRIGNTAwMDAw", + "memo": "" + } + EOF + + FROM=nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl + + nibid tx interchain-accounts controller send-tx \ + connection-0 \ + packet.json \ + --from $FROM \ + --gas auto \ + --gas-adjustment 1.5 \ + --gas-prices 0.025unibi \ + --yes | jq + ``` + +8. Verify that the delegation worked on the host chain + + ```sh + # on nibiru-1 + + nibid q staking delegations nibi124zc9yjjksxrfrzpfvkysl3r8zrlef2rce5ccqt9mavgy66hhzmqtrvvlr | jq + ``` + +9. (Optional) Verify the packet data on the host chain + + ```sh + nibid q interchain-accounts host packet-events channel-1 1 | jq ## Endpoints - `http://localhost:5555` -> GraphQL server -- `http://localhost:26657` -> Tendermint RPC server -- `tcp://localhost:9090` -> Cosmos SDK gRPC server -- `http://localhost:1317` -> Cosmos SDK LCD (REST) server - `http://localhost:8000` -> Faucet server (HTTP POST only) +- +- `http://localhost:26657` -> nibiru-0 Tendermint RPC server +- `tcp://localhost:9090` -> nibiru-0 Cosmos SDK gRPC server +- `http://localhost:1317` -> nibiru-0 Cosmos SDK LCD (REST) server +- +- `http://localhost:36657` -> nibiru-1 Tendermint RPC server +- `tcp://localhost:19090` -> nibiru-1 Cosmos SDK gRPC server +- `http://localhost:11317` -> nibiru-1 Cosmos SDK LCD (REST) server ## FAQ ### `make chaosnet` says that "Additional property name is not allowed" -Make sure to update your docker application to version 23.0.1 \ No newline at end of file +Make sure to update your docker application to version >=23.0.1 + +### Does data persist between runs? + +No, all volumes are deleted and recreated every time you run `make chaosnet`. This is to ensure that you always start with a clean network. + +### My `make chaosnet` takes forever to run + +It usually takes a few minutes to set everything up and create the IBC channels. If it takes more than 5 minutes, then check the logs of the chaosnet containers to see if any step failed. Reach out to for help. diff --git a/Dockerfile b/Dockerfile index a8ef323b9..b81262b7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,8 @@ FROM golang:1.21 AS builder WORKDIR /nibiru -COPY go.sum go.mod ./ +# copy go.mod, go.sum to WORKDIR +COPY go.sum go.mod ./ RUN go mod download COPY . . diff --git a/INSTALL.md b/INSTALL.md index 077ce4964..39461d675 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,14 +1,18 @@ -# Install `nibid` binaries +# Install `nibid` binaries This guide will explain how to install the Nibiru Chain binary, `nibid`, onto your system. #### Table of Contents + - [1. Update the system](#1-update-the-system) - [2. Install Golang](#2-install-golang) - [3. Install build requirements](#3-install-build-requirements) - [4. Clone the Nibiru Repository](#4-clone-the-nibiru-repository) - [Upgrade](#upgrade) - +- [Troubleshooting](#troubleshooting) +- [Contributing](#contributing) +- [Troubleshooting](#troubleshooting-1) + - [Error when running `make install` on MacOS](#error-when-running-make-install-on-macos) ## 1. Update the system @@ -19,7 +23,7 @@ sudo apt update sudo apt upgrade --yes ``` -## 2. Install Golang +## 2. Install Golang Steps described here: https://go.dev/doc/install @@ -34,7 +38,6 @@ wget -q -O - https://git.io/vQhTU | bash -s -- --version 1.16 After installed, open new terminal to properly load go - ## 4. Clone the Nibiru Repository ```sh @@ -43,19 +46,21 @@ git clone https://github.com/NibiruChain/nibiru cd nibiru ``` -On this fresh clone of the repo, simply run +On this fresh clone of the repo, simply run + ```sh -make build +make build make install make localnet ``` -and open another terminal. + +and open another terminal. --- ## Upgrade -The scheduled mainnet upgrade to `nibiru-2` is planned for +The scheduled mainnet upgrade to `nibiru-2` is planned for ``` cd nibiru @@ -63,12 +68,11 @@ git fetch tags git checkout v0.0.1 ``` - - Testnet +Testnet One the Nibiru binary has been installed, for further information on joining the testnet, head over to the [testnet repo](https://github.com/NibiruChain/Networks/tree/main/Testnet). - Mainnet +Mainnet One the Nibiru binary has been installed, for further information on joining mainnet, head over to the [mainnet repo](https://github.com/NibiruChain/Networks/tree/main/Mainnet). @@ -84,9 +88,9 @@ B. New commands you've made on the `nibid` don't show up. Your `nibid` probably ## Contributing -The code for `nibid` is located in the `/cmd/nibid` folder. +The code for `nibid` is located in the `/cmd/nibid` folder. -In addition to the commands available within that folder, `nibid` pulls in cli subcommands from the modules e.g. `/x/perps/cli` +In addition to the commands available within that folder, `nibid` pulls in cli subcommands from the modules e.g. `/x/oracle/cli` After updating the code run @@ -98,15 +102,15 @@ make install To see all the commands available just add `--help` to the end. Example: + ```bash nibid --help nibid query --help nibid tx --help -nibid query perp --help +nibid query oracle --help ``` - -## Troubleshotting +## Troubleshooting ### Error when running `make install` on MacOS @@ -120,4 +124,4 @@ You can fix it by installing `wget` with Homebrew: ``` brew install wget -``` \ No newline at end of file +``` diff --git a/LEGACY-CHANGELOG.md b/LEGACY-CHANGELOG.md new file mode 100644 index 000000000..db38dedbd --- /dev/null +++ b/LEGACY-CHANGELOG.md @@ -0,0 +1,837 @@ +# Nibiru/LEGACY-CHANGELOG + +This file contains older changelog entries for the "`NibiruChain/nibiru`" +repository. + +## [v1.2.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.2.0) - 2024-03-28 + +Nibiru v1.2.0 adds a burn method to the x/inflation module that allows senders to burn tokens. + +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.2.0)] +- [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.2.0)] + +### Features + +- [#1832](https://github.com/NibiruChain/nibiru/pull/1832) - feat(tokenfactory): add burn method for native tokens + +## [v1.1.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.1.0) - 2024-03-19 + +Nibiru v1.1.0 is the minor release used to add inflation to the network. + +### State Machine Breaking + +- [#1786](https://github.com/NibiruChain/nibiru/pull/1786) - fix(inflation): fix inflation off-by 2 error +- [#1796](https://github.com/NibiruChain/nibiru/pull/1796) - fix(inflation): fix num skipped epoch when inflation is added to an existing chain +- [#1797](https://github.com/NibiruChain/nibiru/pull/1797) - fix(inflation): fix num skipped epoch updates logic +- [#1712](https://github.com/NibiruChain/nibiru/pull/1712) - refactor(inflation): turn inflation off by default + +### Bug Fixes + +- [#1706](https://github.com/NibiruChain/nibiru/pull/706) - fix: `v1.1.0` upgrade handler +- [#1804](https://github.com/NibiruChain/nibiru/pull/1804) - fix(inflation): update default parameters +- [#1688](https://github.com/NibiruChain/nibiru/pull/1688) - fix(inflation): make default inflation allocation follow tokenomics + +### Features + +- [#1670](https://github.com/NibiruChain/nibiru/pull/1670) - feat(inflation): Make inflation polynomial +- [#1682](https://github.com/NibiruChain/nibiru/pull/1682) - feat!: add upgrade handler for v1.1.0 +- [#1776](https://github.com/NibiruChain/nibiru/pull/1776) - feat(inflation): make inflation params a collection and add commands to update them +- [#1795](https://github.com/NibiruChain/nibiru/pull/1795) - feat(inflation): add inflation tx cmds + +### Improvements + +- [#1695](https://github.com/NibiruChain/nibiru/pull/1695) - feat(inflation): add events for inflation distribution +- [#1792](https://github.com/NibiruChain/nibiru/pull/1792) - fix(inflation): uncomment legacy amino register on app module basic +- [#1799](https://github.com/NibiruChain/nibiru/pull/1799) refactor,docs(inflation): Document everything + delete unused code. Make perp and spot optional features in localnet.sh + +## [v1.0.3](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.3) - 2024-03-18 + +### Fix + +- [#1816](https://github.com/NibiruChain/nibiru/pull/1816) - fix(ibc): fix ibc transaction from wasm contract + +### CLI + +- [#1731](https://github.com/NibiruChain/nibiru/pull/1731) - feat(cli): add cli command to decode stargate base64 messages +- [#1754](https://github.com/NibiruChain/nibiru/pull/1754) - refactor(decode-base64): clean code improvements and fn docs + +## [v1.0.2](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.2) - 2024-03-03 + +### Dependencies + +- [65c06ba](https://github.com/NibiruChain/nibiru/commit/65c06ba774c260ece942131ad7a93de0e162266e) - Bump `cosmos-sdk` to v0.47.10 + +## [v1.0.1](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.1) - 2024-02-09 + +### Dependencies + +- [#1778](https://github.com/NibiruChain/nibiru/pull/1778) - chore: bump librocksdb to v8.9.1 + +## [v1.0.0](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.0) + +Nibiru v1.0.0 is the major release used for the genesis of the mainnet network, +`cataclysm-1`. It includes all of the general purpose modules such +as `devgas`, `sudo`, `wasm`, `tokenfactory`, and the defaults from Cosmos SDK +v0.47.5. + +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v1.0.0)] +- [[Commits](https://github.com/NibiruChain/nibiru/commits/v1.0.0)] +- [tag:v1.0.0](https://github.com/NibiruChain/nibiru/commits/v1.0.0) epic(v1.0.0): Remove unneeded Dapp modules for smooth upgrades. + - chore!: [Date: 2023-10-16] Remove inflation, perp, stablecoin, and spot + modules and related protobufs. This will make it easier to add the store keys + layer if we have breaking changes before the Dapps go live without blocking + the mainnet deployment. + Commits: + [#1667](https://github.com/NibiruChain/nibiru/pull/1667) + [6a01abe](https://github.com/NibiruChain/nibiru/commit/6a01abe5c99e26a1d17d96f359d06d61bc1e6e70) + [2a250a3](https://github.com/NibiruChain/nibiru/commit/2a250a3c4c60c58c5526ac7d75ce5b9e13889471) + [d713f41](https://github.com/NibiruChain/nibiru/commit/d713f41dfe17d6d29451ade4d2f0e6d950ce7c59) + [011f1ed](https://github.com/NibiruChain/nibiru/commit/011f1ed431d92899d01583e5e6110e663eceaa24) + +### Features + +- [#1596](https://github.com/NibiruChain/nibiru/pull/1596) - epic(tokenfactory): State transitions, collections, genesis import and export, and app wiring +- [#1607](https://github.com/NibiruChain/nibiru/pull/1607) - Token factory transaction messages for CreateDenom, ChangeAdmin, and UpdateModuleParams +- [#1620](https://github.com/NibiruChain/nibiru/pull/1620) - Token factory transaction messages for Mint and Burn +- [#1573](https://github.com/NibiruChain/nibiru/pull/1573) - feat(perp): Close markets and compute settlement price + +### State Machine Breaking + +- [#1609](https://github.com/NibiruChain/nibiru/pull/1609) - refactor(app)!: Remove x/stablecoin module. +- [#1613](https://github.com/NibiruChain/nibiru/pull/1613) - feat(app)!: enforce min commission by changing default and genesis validation +- [#1615](https://github.com/NibiruChain/nibiru/pull/1613) - feat(ante)!: Ante handler to add a maximum commission rate of 25% for validators. +- [#1616](https://github.com/NibiruChain/nibiru/pull/1616) - fix(app)!: Add custom wasm snapshotter for proper state exports +- [#1617](https://github.com/NibiruChain/nibiru/pull/1617) - fix(app)!: non-nil snapshot manager is not guaranteed in testapp +- [#1645](https://github.com/NibiruChain/nibiru/pull/1645) - fix(tokenfactory)!: token supply in bank keeper must be correct after MsgBurn. +- [#1646](https://github.com/NibiruChain/nibiru/pull/1646) - feat(wasmbinding)!: whitelisted stargate queries for QueryRequest::Stargate: auth, bank, gov, tokenfactory, epochs, inflation, oracle, sudo, devgas +- [#1667](https://github.com/NibiruChain/nibiru/pull/1667) - chore(inflation)!: unwire x/inflation + +### Improvements + +- [#1610](https://github.com/NibiruChain/nibiru/pull/1610) - refactor(app): Simplify app.go with less redundant imports using struct embedding. +- [#1614](https://github.com/NibiruChain/nibiru/pull/1614) - refactor(proto): Use explicit namespacing on proto imports for #1608 +- [#1630](https://github.com/NibiruChain/nibiru/pull/1630) - refactor(wasm): clean up wasmbinding/ folder structure +- [#1631](https://github.com/NibiruChain/nibiru/pull/1631) - fix(.goreleaser.yml): Load version for wasmvm dynamically. +- [#1638](https://github.com/NibiruChain/nibiru/pull/1638) - test(tokenfactory): integration test core logic with a real smart contract using `nibiru-std` +- [#1659](https://github.com/NibiruChain/nibiru/pull/1659) - refactor(oracle): curate oracle default whitelist + +### Dependencies + +- Bump `github.com/prometheus/client_golang` from 1.16.0 to 1.17.0 ([#1605](https://github.com/NibiruChain/nibiru/pull/1605)) +- Bump `bufbuild/buf-setup-action` from 1.26.1 to 1.27.1 ([#1624](https://github.com/NibiruChain/nibiru/pull/1624), [#1641](https://github.com/NibiruChain/nibiru/pull/1641)) +- Bump `stefanzweifel/git-auto-commit-action` from 4 to 5 ([#1625](https://github.com/NibiruChain/nibiru/pull/1625)) +- Bump `github.com/CosmWasm/wasmvm` from 1.4.0 to 1.5.0 ([#1629](https://github.com/NibiruChain/nibiru/pull/1629), [#1657](https://github.com/NibiruChain/nibiru/pull/1657)) +- Bump `google.golang.org/grpc` from 1.58.2 to 1.59.0 ([#1633](https://github.com/NibiruChain/nibiru/pull/1633), [#1643](https://github.com/NibiruChain/nibiru/pull/1643)) +- Bump `golang.org/x/net` from 0.12.0 to 0.17.0 ([#1634](https://github.com/NibiruChain/nibiru/pull/1634)) +- Bump `github.com/cosmos/ibc-go/v7` from 7.3.0 to 7.3.1 ([#1647](https://github.com/NibiruChain/nibiru/pull/1647)) +- Bump `github.com/CosmWasm/wasmd` from 0.40.2 to 0.43.0 ([#1660](https://github.com/NibiruChain/nibiru/pull/1660)) +- Bump `github.com/CosmWasm/wasmd` from 0.43.0 to 0.44.0 ([#1666](https://github.com/NibiruChain/nibiru/pull/1666)) + +### Bug Fixes + +- [#1606](https://github.com/NibiruChain/nibiru/pull/1606) - fix(perp): emit `MarketUpdatedEvent` in the absence of index price +- [#1649](https://github.com/NibiruChain/nibiru/pull/1649) - fix(ledger): fix ledger for newer macos versions +- [#1655](https://github.com/NibiruChain/nibiru/pull/1655) - fix(inflation): inflate NIBI correctly to strategic treasury account + +## [v0.21.11] - 2023-10-02 + +NOTE: It's pragmatic to assume that any change prior to v1.0.0 was state machine breaking. + +- Summary: Changes up to pull request #1616 +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v0.21.11)] + +## [v0.21.10] - 2023-09-20 + +- Summary: Changes up to pull request #1595 +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v0.21.10)] + +### State Machine Breaking + +#### Features (Breaking) + +- [#1594](https://github.com/NibiruChain/nibiru/pull/1594) - feat: add user discounts +- [#1585](https://github.com/NibiruChain/nibiru/pull/1585) - feat: include flag versioned in query markets to allow to query disabled markets +- [#1575](https://github.com/NibiruChain/nibiru/pull/1575) - feat(perp): Add trader volume tracking +- [#1559](https://github.com/NibiruChain/nibiru/pull/1559) - feat: add versions to markets to allow to disable them +- [#1543](https://github.com/NibiruChain/nibiru/pull/1543) - epic(devgas): devgas module for incentivizing smart contract +- [#1543](https://github.com/NibiruChain/nibiru/pull/1543) - epic(devgas): devgas module for incentivizing smart contract +- [#1541](https://github.com/NibiruChain/nibiru/pull/1541) - feat(perp): add clamp to premium fractions +- [#1520](https://github.com/NibiruChain/nibiru/pull/1520) - feat(wasm): no op handler + tests with updated contracts +- [#1503](https://github.com/NibiruChain/nibiru/pull/1503) - feat(wasm): add Oracle Exchange Rate query for wasm +- [#1503](https://github.com/NibiruChain/nibiru/pull/1503) - feat(wasm): add Oracle Exchange Rate query for wasm +- [#1502](https://github.com/NibiruChain/nibiru/pull/1502) - feat: add ledger build support +- [#1501](https://github.com/NibiruChain/nibiru/pull/1501) - feat(proto): add Python buf generation logic for py-sdk +- [#1501](https://github.com/NibiruChain/nibiru/pull/1501) - feat(proto): add Python buf generation logic for py-sdk +- [#1501](https://github.com/NibiruChain/nibiru/pull/1501) - feat(localnet.sh): (1) Make it possible to run while offline. (2) Implement --no-build option to use the script with the current `nibid` installed. +- [#1501](https://github.com/NibiruChain/nibiru/pull/1501) - feat(localnet.sh): (1) Make it possible to run while offline. (2) Implement --no-build option to use the script with the current `nibid` installed. +- [#1498](https://github.com/NibiruChain/nibiru/pull/1498) - feat: add cli to change root sudo command +- [#1498](https://github.com/NibiruChain/nibiru/pull/1498) - feat: add cli to change root sudo command +- [#1495](https://github.com/NibiruChain/nibiru/pull/1495) - feat: add genmsg module +- [#1494](https://github.com/NibiruChain/nibiru/pull/1494) - feat: create cli to add sudo account into genesis +- [#1479](https://github.com/NibiruChain/nibiru/pull/1479) - feat(perp): implement `PartialClose` +- [#1479](https://github.com/NibiruChain/nibiru/pull/1479) - feat(perp): implement `PartialClose` +- [#1463](https://github.com/NibiruChain/nibiru/pull/1463) - feat(oracle): add genesis pricefeeder delegation +- [#1463](https://github.com/NibiruChain/nibiru/pull/1463) - feat(oracle): add genesis pricefeeder delegation +- [#1463](https://github.com/NibiruChain/nibiru/pull/1463) - feat(oracle): add genesis pricefeeder delegation +- [#1421](https://github.com/NibiruChain/nibiru/pull/1421) - feat(oracle): add expiry time to oracle prices +- [#1407](https://github.com/NibiruChain/nibiru/pull/1407) - feat!: upgrade to Cosmos SDK v0.47.3 +- [#1407](https://github.com/NibiruChain/nibiru/pull/1407) - feat!: upgrade to Cosmos SDK v0.47.3 +- [#1387](https://github.com/NibiruChain/nibiru/pull/1387) - feat: upgrade to Cosmos SDK v0.46.10 +- [#1387](https://github.com/NibiruChain/nibiru/pull/1387) - feat: upgrade to Cosmos SDK v0.46.10 +- [#1380](https://github.com/NibiruChain/nibiru/pull/1380) - feat(wasm): Add CreateMarket admin call for the controller contract +- [#1380](https://github.com/NibiruChain/nibiru/pull/1380) - feat(wasm): Add CreateMarket admin call for the controller contract +- [#1373](https://github.com/NibiruChain/nibiru/pull/1373) - feat(perp): `perpv2` `add-genesis-perp-market` CLI command +- [#1371](https://github.com/NibiruChain/nibiru/pull/1371) - feat: realize bad debt when a user tries to close his position +- [#1370](https://github.com/NibiruChain/nibiru/pull/1370) - feat(perp): `perpv2` `CreatePool` method +- [#1367](https://github.com/NibiruChain/nibiru/pull/1367) - feat: wire enable market to wasm +- [#1367](https://github.com/NibiruChain/nibiru/pull/1367) - feat: wire enable market to wasm +- [#1366](https://github.com/NibiruChain/nibiru/pull/1366) - feat: fix bindings test in cw_test +- [#1363](https://github.com/NibiruChain/nibiru/pull/1363) - feat(perp): wire `PerpV2` module +- [#1362](https://github.com/NibiruChain/nibiru/pull/1362) - feat(perp): add `perpv2` cli +- [#1361](https://github.com/NibiruChain/nibiru/pull/1361) - feat(perp): add `PerpV2` module +- [#1359](https://github.com/NibiruChain/nibiru/pull/1359) - feat(perp): Add InsuranceFundWithdraw admin call with corresponding smart contract +- [#1359](https://github.com/NibiruChain/nibiru/pull/1359) - feat(perp): Add InsuranceFundWithdraw admin call with corresponding smart contract +- [#1352](https://github.com/NibiruChain/nibiru/pull/1352) - feat(perp): add PerpKeeperV2 `MsgServer` +- [#1350](https://github.com/NibiruChain/nibiru/pull/1350) - feat(perp): `EditPriceMultiplier` and `EditSwapInvariant` +- [#1345](https://github.com/NibiruChain/nibiru/pull/1345) - feat(perp): PerpV2 QueryServer +- [#1344](https://github.com/NibiruChain/nibiru/pull/1344) - feat(perp): PerpKeeperV2 `AddMargin` and `RemoveMargin` +- [#1343](https://github.com/NibiruChain/nibiru/pull/1343) - feat(perp): add PerpKeeperV2 `MultiLiquidate` +- [#1342](https://github.com/NibiruChain/nibiru/pull/1342) - feat(perp): market not enabled can only be used to close out existing positions +- [#1342](https://github.com/NibiruChain/nibiru/pull/1342) - feat(perp): market not enabled can only be used to close out existing positions +- [#1341](https://github.com/NibiruChain/nibiru/pull/1341) - feat(bindings/oracle): add bindings for oracle module params +- [#1340](https://github.com/NibiruChain/nibiru/pull/1340) - feat(wasm): Enforce x/sudo contract permission checks on the shifter contract + integration tests +- [#1338](https://github.com/NibiruChain/nibiru/pull/1338) - feat(perp): V2 OpenPosition + +#### Bug Fixes (Breaking) + +- [#1586](https://github.com/NibiruChain/nibiru/pull/1586) - fix(sudo): make messages compatible with `Amino` +- [#1565](https://github.com/NibiruChain/nibiru/pull/1565) - fix(oracle)!: Count vote omission as abstain for less slashing + more stability +- [#1493](https://github.com/NibiruChain/nibiru/pull/1493) - fix(perp): allow `ClosePosition` when there is bad debt +- [#1476](https://github.com/NibiruChain/nibiru/pull/1476) - fix(wasm)!: call `ValidateBasic` before all `sdk.Msg` calls for the bindings-perp contract + remove sudo permissioning +- [#1467](https://github.com/NibiruChain/nibiru/pull/1467) - fix(oracle): make `calcTwap` safer +- [#1467](https://github.com/NibiruChain/nibiru/pull/1467) - fix(oracle): make `calcTwap` safer +- [#1464](https://github.com/NibiruChain/nibiru/pull/1464) - fix(gov): wire legacy proposal handlers +- [#1464](https://github.com/NibiruChain/nibiru/pull/1464) - fix(gov): wire legacy proposal handlers +- [#1459](https://github.com/NibiruChain/nibiru/pull/1459) - fix(spot): wire `x/spot` msgService into app router +- [#1459](https://github.com/NibiruChain/nibiru/pull/1459) - fix(spot): wire `x/spot` msgService into app router +- [#1452](https://github.com/NibiruChain/nibiru/pull/1452) - fix(oracle): continue with abci hook during error +- [#1451](https://github.com/NibiruChain/nibiru/pull/1451) - fix(perp): decrease position with zero size +- [#1446](https://github.com/NibiruChain/nibiru/pull/1446) - fix(cmd): Add custom InitCmd to set set desired Tendermint consensus params for each node. +- [#1441](https://github.com/NibiruChain/nibiru/pull/1441) - fix(oracle): ignore abstain votes in std dev calculation +- [#1425](https://github.com/NibiruChain/nibiru/pull/1425) - fix: remove positions from state when closed with reverse position +- [#1423](https://github.com/NibiruChain/nibiru/pull/1423) - fix: remove panics from abci hooks +- [#1422](https://github.com/NibiruChain/nibiru/pull/1422) - fix(oracle): handle zero oracle rewards +- [#1420](https://github.com/NibiruChain/nibiru/pull/1420) - refactor(oracle): update default params +- [#1419](https://github.com/NibiruChain/nibiru/pull/1419) - fix(spot): add pools to genesis state +- [#1417](https://github.com/NibiruChain/nibiru/pull/1417) - fix: run end blocker on block end for perp v2 +- [#1414](https://github.com/NibiruChain/nibiru/pull/1414) - fix(oracle): Add deterministic map iterations to avoid consensus failure. +- [#1413](https://github.com/NibiruChain/nibiru/pull/1413) - fix(perp): provide descriptive errors when all liquidations fail in MultiLiquidate +- [#1413](https://github.com/NibiruChain/nibiru/pull/1413) - fix(perp): provide descriptive errors when all liquidations fail in MultiLiquidate +- [#1397](https://github.com/NibiruChain/nibiru/pull/1397) - fix: ensure margin is high enough when removing it +- [#1383](https://github.com/NibiruChain/nibiru/pull/1383) - feat: enforce contract to be whitelisted when calling perp bindings +- [#1379](https://github.com/NibiruChain/nibiru/pull/1379) - feat(perp): check for denom in add/remove margin +- [#1210](https://github.com/NibiruChain/nibiru/pull/1210) - fix(ci): fix docker push workflow + +#### Else (Breaking) + +- [#1477](https://github.com/NibiruChain/nibiru/pull/1477) - refactor(oracle)!: Move away from deprecated events to typed events in x/oracle +- [#1477](https://github.com/NibiruChain/nibiru/pull/1477) - refactor(oracle)!: Move away from deprecated events to typed events in x/oracle +- [#1473](https://github.com/NibiruChain/nibiru/pull/1473) - refactor(perp)!: rename `OpenPosition` to `MarketOrder` +- [#1473](https://github.com/NibiruChain/nibiru/pull/1473) - refactor(perp)!: rename `OpenPosition` to `MarketOrder` +- [#1427](https://github.com/NibiruChain/nibiru/pull/1427) - refactor(perp)!: PositionChangedEvent `MarginToUser` +- [#1427](https://github.com/NibiruChain/nibiru/pull/1427) - refactor(perp)!: PositionChangedEvent `MarginToUser` +- [#1426](https://github.com/NibiruChain/nibiru/pull/1426) - refactor(perp): remove price fluctuation limit check +- [#1388](https://github.com/NibiruChain/nibiru/pull/1388) - refactor(perp)!: idempotent position changed event +- [#1388](https://github.com/NibiruChain/nibiru/pull/1388) - refactor(perp)!: idempotent position changed event +- [#1385](https://github.com/NibiruChain/nibiru/pull/1385) - test(perp): add clearing house negative tests +- [#1385](https://github.com/NibiruChain/nibiru/pull/1385) - test(perp): add clearing house negative tests +- [#1382](https://github.com/NibiruChain/nibiru/pull/1382) - refactor(perp)!: remove `perpv1` +- [#1382](https://github.com/NibiruChain/nibiru/pull/1382) - refactor(perp)!: remove `perpv1` +- [#1356](https://github.com/NibiruChain/nibiru/pull/1356) - build: Regress wasmvm (v1.1.1), tendermint (v0.34.24), and Cosmos-SDK (v0.45.14) dependencies +- [#1356](https://github.com/NibiruChain/nibiru/pull/1356) - build: Regress wasmvm (v1.1.1), tendermint (v0.34.24), and Cosmos-SDK (v0.45.14) dependencies +- [#1346](https://github.com/NibiruChain/nibiru/pull/1346) - build: Upgrade wasmvm (v1.2.1), tendermint (v0.34.26), and Cosmos-SDK (v0.45.14) dependencies +- [#1346](https://github.com/NibiruChain/nibiru/pull/1346) - build: Upgrade wasmvm (v1.2.1), tendermint (v0.34.26), and Cosmos-SDK (v0.45.14) dependencies + +### Non-breaking/Compatible Improvements + +- [#1579](https://github.com/NibiruChain/nibiru/pull/1579) - chore(proto): Add a buf.gen.rs.yaml and corresponding script to create Rust types for Wasm Stargate messages +- [#1574](https://github.com/NibiruChain/nibiru/pull/1574) - chore(goreleaser): update wasmvm to v1.4.0 +- [#1558](https://github.com/NibiruChain/nibiru/pull/1558) - feat(perp): paginated query to read the position store +- [#1555](https://github.com/NibiruChain/nibiru/pull/1555) - feat(devgas): Convert legacy ABCI events to typed proto events +- [#1554](https://github.com/NibiruChain/nibiru/pull/1554) - refactor: runs gofumpt formatter, which has nice conventions: go install mvdan.cc/gofumpt@latest +- [#1536](https://github.com/NibiruChain/nibiru/pull/1536) - test(perp): add more tests to perp module and cli +- [#1533](https://github.com/NibiruChain/nibiru/pull/1533) - feat(perp): add differential fields to PositionChangedEvent +- [#1527](https://github.com/NibiruChain/nibiru/pull/1527) - test(common): add docs for testutil and increase test coverage +- [#1521](https://github.com/NibiruChain/nibiru/pull/1521) - test(sudo): increase unit test coverage +- [#1519](https://github.com/NibiruChain/nibiru/pull/1519) - test: add more tests to x/perp keeper +- [#1518](https://github.com/NibiruChain/nibiru/pull/1518) - test: add more tests to x/perp +- [#1517](https://github.com/NibiruChain/nibiru/pull/1517) - test: add more tests to x/hooks +- [#1506](https://github.com/NibiruChain/nibiru/pull/1506) - refactor(oracle): Implement OrderedMap and use it for iterating through maps in x/oracle +- [#1500](https://github.com/NibiruChain/nibiru/pull/1500) - refactor(perp): clean up reverse market order mechanics +- [#1466](https://github.com/NibiruChain/nibiru/pull/1466) - refactor(perp): `PositionLiquidatedEvent` +- [#1466](https://github.com/NibiruChain/nibiru/pull/1466) - refactor(perp): `PositionLiquidatedEvent` +- [#1462](https://github.com/NibiruChain/nibiru/pull/1462) - fix(perp): Add pair to liquidation failed event. +- [#1424](https://github.com/NibiruChain/nibiru/pull/1424) - feat(perp): Add change type and exchanged margin to position changed events. +- [#1408](https://github.com/NibiruChain/nibiru/pull/1408) - feat(spot): idempotent events +- [#1406](https://github.com/NibiruChain/nibiru/pull/1406) - feat(perp): emit additional event info +- [#1405](https://github.com/NibiruChain/nibiru/pull/1405) - ci: use Buf to build protos +- [#1390](https://github.com/NibiruChain/nibiru/pull/1390) - fix(localnet.sh): Fix genesis market initialization + add force exits on failure +- [#1369](https://github.com/NibiruChain/nibiru/pull/1369) - refactor(oracle): divert rewards from `perpv2` instead of `perpv1` +- [#1365](https://github.com/NibiruChain/nibiru/pull/1365) - refactor(perp): split `perp` module into v1/ and v2/ + +### Dependencies + +- [#1523](https://github.com/NibiruChain/nibiru/pull/1523) - chore: bump cosmos-sdk to v0.47.4 +- [#1381](https://github.com/NibiruChain/nibiru/pull/1381) - chore(deps): Bump github.com/cosmos/cosmos-sdk to 0.45.16 + +- Bump `github.com/docker/distribution` from 2.8.1+incompatible to 2.8.2+incompatible (#1339) + +- Bump `github.com/CosmWasm/wasmvm` from 1.2.1 to 1.4.0 (#1354, #1507, [#1564](https://github.com/NibiruChain/nibiru/pull/1564)) +- Bump `github.com/spf13/cast` from 1.5.0 to 1.5.1 (#1358) +- Bump `github.com/stretchr/testify` from 1.8.2 to 1.8.4 (#1384, #1435) +- Bump `cosmossdk.io/math` from 1.0.0-beta.6 to 1.1.2 (#1394, [#1547](https://github.com/NibiruChain/nibiru/pull/1547)) +- Bump `google.golang.org/grpc` from 1.53.0 to 1.58.2 (#1395, #1437, #1443, #1497, [#1525](https://github.com/NibiruChain/nibiru/pull/1525), [#1568](https://github.com/NibiruChain/nibiru/pull/1568), [#1582](https://github.com/NibiruChain/nibiru/pull/1582), [#1598](https://github.com/NibiruChain/nibiru/pull/1598)) +- Bump `github.com/gin-gonic/gin` from 1.8.1 to 1.9.1 (#1409) +- Bump `github.com/spf13/viper` from 1.15.0 to 1.16.0 (#1436) +- Bump `github.com/prometheus/client_golang` from 1.15.1 to 1.16.0 (#1431) +- Bump `github.com/cosmos/ibc-go/v7` from 7.1.0 to 7.3.0 (#1445, [#1562](https://github.com/NibiruChain/nibiru/pull/1562)) +- Bump `bufbuild/buf-setup-action` from 1.21.0 to 1.26.1 (#1449, #1469, #1505, #1510, [#1537](https://github.com/NibiruChain/nibiru/pull/1537), [#1540](https://github.com/NibiruChain/nibiru/pull/1540), [#1544](https://github.com/NibiruChain/nibiru/pull/1544)) +- Bump `google.golang.org/protobuf` from 1.30.0 to 1.31.0 (#1450) +- Bump `cosmossdk.io/errors` from 1.0.0-beta.7 to 1.0.0 (#1499) +- Bump `github.com/holiman/uint256` from 1.2.2 to 1.2.3 (#1504) +- Bump `docker/build-push-action` from 4 to 5 ([#1572](https://github.com/NibiruChain/nibiru/pull/1572)) +- Bump `docker/login-action` from 2 to 3 ([#1571](https://github.com/NibiruChain/nibiru/pull/1571)) +- Bump `docker/setup-buildx-action` from 2 to 3 ([#1570](https://github.com/NibiruChain/nibiru/pull/1570)) +- Bump `docker/setup-qemu-action` from 2 to 3 ([#1569](https://github.com/NibiruChain/nibiru/pull/1569)) +- Bump `github.com/cosmos/cosmos-sdk` from v0.47.4 to v0.47.5 ([#1578](https://github.com/NibiruChain/nibiru/pull/1578)) +- Bump `codecov/codecov-action` from 3 to 4 ([#1583](https://github.com/NibiruChain/nibiru/pull/1583)) +- Bump `actions/checkout` from 3 to 4 ([#1593](https://github.com/NibiruChain/nibiru/pull/1593)) +- Bump `github.com/docker/distribution` from 2.8.1+incompatible to 2.8.2+incompatible (#1339) +- Bump `github.com/CosmWasm/wasmvm` from 1.2.1 to 1.3.0 (#1354, #1507) +- Bump `github.com/spf13/cast` from 1.5.0 to 1.5.1 (#1358) +- Bump `github.com/stretchr/testify` from 1.8.2 to 1.8.4 (#1384, #1435) +- Bump `cosmossdk.io/math` from 1.0.0-beta.6 to 1.1.2 (#1394, [#1547](https://github.com/NibiruChain/nibiru/pull/1547)) +- Bump `google.golang.org/grpc` from 1.53.0 to 1.57.0 (#1395, #1437, #1443, #1497, [#1525](https://github.com/NibiruChain/nibiru/pull/1525)) +- Bump `github.com/gin-gonic/gin` from 1.8.1 to 1.9.1 (#1409) +- Bump `github.com/spf13/viper` from 1.15.0 to 1.16.0 (#1436) +- Bump `github.com/prometheus/client_golang` from 1.15.1 to 1.16.0 (#1431) +- Bump `github.com/cosmos/ibc-go/v7` from 7.1.0 to 7.3.0 (#1445, [#1562](https://github.com/NibiruChain/nibiru/pull/1562)) +- Bump `bufbuild/buf-setup-action` from 1.21.0 to 1.26.1 (#1449, #1469, #1505, #1510, [#1537](https://github.com/NibiruChain/nibiru/pull/1537), [#1540](https://github.com/NibiruChain/nibiru/pull/1540), [#1544](https://github.com/NibiruChain/nibiru/pull/1544)) +- Bump `google.golang.org/protobuf` from 1.30.0 to 1.31.0 (#1450) +- Bump `cosmossdk.io/errors` from 1.0.0-beta.7 to 1.0.0 (#1499) +- Bump `github.com/holiman/uint256` from 1.2.2 to 1.2.3 (#1504) +- Bump `actions/checkout` from 3 to 4 ([#1563](https://github.com/NibiruChain/nibiru/pull/1563)) + +## [v0.19.4] - 2023-05-26 + +- Summary: Changes up to pull request #1337 +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v0.19.4)] + +### State Machine Breaking + +- [#1336](https://github.com/NibiruChain/nibiru/pull/1336) - feat: move oracle params out of params subspace and onto the keeper +- [#1336](https://github.com/NibiruChain/nibiru/pull/1336) - feat: move oracle params out of params subspace and onto the keeper +- [#1335](https://github.com/NibiruChain/nibiru/pull/1335) - refactor(perp): move remaining perpv1 files to v1 directory +- [#1334](https://github.com/NibiruChain/nibiru/pull/1334) - feat(perp): add PerpKeeperV2 `ClosePosition` +- [#1333](https://github.com/NibiruChain/nibiru/pull/1333) - feat(perp): add basic clearing house functions +- [#1332](https://github.com/NibiruChain/nibiru/pull/1332) - feat(perp): add hooks to update funding rate +- [#1331](https://github.com/NibiruChain/nibiru/pull/1331) - refactor(perp): create perp v1 type package and module package +- [#1329](https://github.com/NibiruChain/nibiru/pull/1329) - feat(perp): add PerpKeeperV2 withdraw methods +- [#1328](https://github.com/NibiruChain/nibiru/pull/1328) - feat(perp): add PerpKeeperV2 swap methods +- [#1322](https://gitub.com/NibiruChain/nibiru/pull/1322) - build(deps): Bumps github.com/armon/go-metrics from 0.4.0 to 0.4.1. +- [#1319](https://github.com/NibiruChain/nibiru/pull/1319) - test: add integration test actions +- [#1317](https://github.com/NibiruChain/nibiru/pull/1317) - feat(testutil): Use secp256k1 algo for private key generation in common/testutil. +- [#1317](https://github.com/NibiruChain/nibiru/pull/1317) - feat(sudo): Implement and test CLI commands for tx and queries. +- [#1317](https://github.com/NibiruChain/nibiru/pull/1317) - feat(sudo): Implement and test CLI commands for tx and queries. +- [#1315](https://github.com/NibiruChain/nibiru/pull/1315) - feat: oracle rewards distribution every week +- [#1315](https://github.com/NibiruChain/nibiru/pull/1315) - feat: oracle rewards distribution every week +- [#1312](https://github.com/NibiruChain/nibiru/pull/1312) - feat(wasm): wire depth shift handler to the wasm router +- [#1312](https://github.com/NibiruChain/nibiru/pull/1312) - feat(wasm): wire depth shift handler to the wasm router +- [#1311](https://github.com/NibiruChain/nibiru/pull/1311) - feat(perp): add PerpKeeperV2 +- [#1311](https://github.com/NibiruChain/nibiru/pull/1311) - feat(perp): add Calc and Twap methods +- [#1309](https://github.com/NibiruChain/nibiru/pull/1309) - feat: minimum swap amount set to $1 +- [#1309](https://github.com/NibiruChain/nibiru/pull/1309) - feat: minimum swap amount set to $1 +- [#1308](https://github.com/NibiruChain/nibiru/pull/1308) - feat(perp): ensure there's no int overflow in liq depth calculation +- [#1307](https://github.com/NibiruChain/nibiru/pull/1307) - feat(sudo): Create the x/sudo module + integration tests +- [#1307](https://github.com/NibiruChain/nibiru/pull/1307) - feat(sudo): Create the x/sudo module + integration tests +- [#1306](https://github.com/NibiruChain/nibiru/pull/1306) - feat(perp): complete perp v2 types +- [#1306](https://github.com/NibiruChain/nibiru/pull/1306) - feat(perp): complete perp v2 types +- [#1305](https://github.com/NibiruChain/nibiru/pull/1305) - refactor(perp!): Remove unnecessary protos +- [#1305](https://github.com/NibiruChain/nibiru/pull/1305) - refactor(perp!): Remove unnecessary protos +- [#1304](https://github.com/NibiruChain/nibiru/pull/1304) - feat: db backend - rocksdb +- [#1304](https://github.com/NibiruChain/nibiru/pull/1304) - feat: db backend - rocksdb +- [#1302](https://github.com/NibiruChain/nibiru/pull/1302) - refactor(oracle)!: price snapshot start time inclusive +- [#1302](https://github.com/NibiruChain/nibiru/pull/1302) - refactor(oracle)!: price snapshot start time inclusive +- [#1301](https://github.com/NibiruChain/nibiru/pull/1301) - fix(epochs)!: correct epoch start time +- [#1301](https://github.com/NibiruChain/nibiru/pull/1301) - fix(epochs)!: correct epoch start time +- [#1299](https://github.com/NibiruChain/nibiru/pull/1299) - feat(wasm): Add peg shift bindings +- [#1299](https://github.com/NibiruChain/nibiru/pull/1299) - feat(wasm): Add peg shift bindings +- [#1298](https://github.com/NibiruChain/nibiru/pull/1298) - refactor(perp)!: remove `MaxOracleSpreadRatio` from Perpv2 +- [#1298](https://github.com/NibiruChain/nibiru/pull/1298) - refactor(perp)!: remove `MaxOracleSpreadRatio` from Perpv2 +- [#1296](https://github.com/NibiruChain/nibiru/pull/1296) - refactor(perp)!: update perp v2 state protos +- [#1296](https://github.com/NibiruChain/nibiru/pull/1296) - refactor(perp)!: update perp v2 state protos +- [#1295](https://github.com/NibiruChain/nibiru/pull/1295) - refactor(app): Organize keepers, store keys, and module manager initialization in app.go +- [#1292](https://github.com/NibiruChain/nibiru/pull/1292) - feat(wasm): Add module bindings for execute calls in x/perp: OpenPosition, ClosePosition, AddMargin, RemoveMargin. +- [#1292](https://github.com/NibiruChain/nibiru/pull/1292) - feat(wasm): Add module bindings for execute calls in x/perp: OpenPosition, ClosePosition, AddMargin, RemoveMargin. +- [#1291](https://github.com/NibiruChain/nibiru/pull/1291) - refactor(perp)!: add perp v2 state protos +- [#1291](https://github.com/NibiruChain/nibiru/pull/1291) - refactor(perp)!: add perp v2 state protos +- [#1290](https://github.com/NibiruChain/nibiru/pull/1290) - refactor: fix quote/base reserve naming convention +- [#1289](https://github.com/NibiruChain/nibiru/pull/1289) - feat: SqrtDepth equal to base reserves when pool creation +- [#1287](https://github.com/NibiruChain/nibiru/pull/1287) - feat(wasm): Add module bindings for custom queries in x/perp: Reserves, AllMarkets, BasePrice, PremiumFraction, Metrics, PerpParams, PerpModuleAccounts +- [#1287](https://github.com/NibiruChain/nibiru/pull/1287) - feat(wasm): Add module bindings for custom queries in x/perp: Reserves, AllMarkets, BasePrice, PremiumFraction, Metrics, PerpParams, PerpModuleAccounts +- [#1286](https://github.com/NibiruChain/nibiru/pull/1286) - feat: bias is zero when creating pool +- [#1284](https://github.com/NibiruChain/nibiru/pull/1284) - feat: fails if base and quote reserves are not equal on CreatePool +- [#1282](https://github.com/NibiruChain/nibiru/pull/1282) - feat(inflation)!: add inflation module +- [#1282](https://github.com/NibiruChain/nibiru/pull/1282) - feat(inflation)!: add inflation module +- [#1281](https://github.com/NibiruChain/nibiru/pull/1281) - feat: add peg multiplier to the pricing logic +- [#1281](https://github.com/NibiruChain/nibiru/pull/1281) - feat: add peg multiplier to the pricing logic +- [#1271](https://github.com/NibiruChain/nibiru/pull/1271) - refactor(perp)!: vpool → perp/amm #2 | imports and renames +- [#1271](https://github.com/NibiruChain/nibiru/pull/1271) - refactor(perp)!: vpool → perp/amm #2 | imports and renames +- [#1270](https://github.com/NibiruChain/nibiru/pull/1270) - refactor(proto)!: lint protos and standardize versioning +- [#1270](https://github.com/NibiruChain/nibiru/pull/1270) - refactor(proto)!: lint protos and standardize versioning +- [#1269](https://github.com/NibiruChain/nibiru/pull/1269) - refactor(perp)!: merge x/util with x/perp +- [#1269](https://github.com/NibiruChain/nibiru/pull/1269) - refactor(perp)!: merge x/util with x/perp +- [#1267](https://github.com/NibiruChain/nibiru/pull/1267) - refactor(perp)!: vpool → perp/amm #1 | Moves types, keeper, and cli +- [#1267](https://github.com/NibiruChain/nibiru/pull/1267) - refactor(perp)!: vpool → perp/amm #1 | Moves types, keeper, and cli +- [#1255](https://github.com/NibiruChain/nibiru/pull/1255) - feat: add peg multiplier field into vpool, which for now defaults to 1 +- [#1255](https://github.com/NibiruChain/nibiru/pull/1255) - feat: add peg multiplier field into vpool, which for now defaults to 1 +- [#1254](https://github.com/NibiruChain/nibiru/pull/1254) - feat: add bias field into vpool +- [#1254](https://github.com/NibiruChain/nibiru/pull/1254) - feat: add bias field into vpool +- [#1248](https://github.com/NibiruChain/nibiru/pull/1248) - refactor(common): Combine x/testutil and x/common/testutil. +- [#1245](https://github.com/NibiruChain/nibiru/pull/1245) - fix(localnet.sh): force localnet.sh to work even if Coingecko is down +- [#1244](https://github.com/NibiruChain/nibiru/pull/1244) - feat: add typed event for oracle post price +- [#1243](https://github.com/NibiruChain/nibiru/pull/1243) - feat(vpool): sqrt of liquidity depth tracked on pool +- [#1243](https://github.com/NibiruChain/nibiru/pull/1243) - feat(vpool): sqrt of liquidity depth tracked on pool +- [#1240](https://github.com/NibiruChain/nibiru/pull/1240) - ci: Test `make proto-gen` when the proto gen scripts or .proto files change +- [#1237](https://github.com/NibiruChain/nibiru/pull/1237) - feat: reduce gas on openposition +- [#1229](https://github.com/NibiruChain/nibiru/pull/1229) - feat: upgrade ibc to v4.2.0 and wasm v0.30.0 +- [#1229](https://github.com/NibiruChain/nibiru/pull/1229) - feat: upgrade ibc to v4.2.0 and wasm v0.30.0 +- [#1228](https://github.com/NibiruChain/nibiru/pull/1228) - feat: update github.com/CosmWasm/wasmd 0.29.2 +- [#1220](https://github.com/NibiruChain/nibiru/pull/1220) - feat: reduce gas fees when posting price +- [#1220](https://github.com/NibiruChain/nibiru/pull/1220) - feat: reduce gas fees when posting price +- [#1219](https://github.com/NibiruChain/nibiru/pull/1219) - fix(ci): use chaosnet image on chaosnet docker compose +- [#1212](https://github.com/NibiruChain/nibiru/pull/1212) - fix(spot): gracefully handle join spot pool with wrong tokens denom + +### Non-breaking/Compatible Improvements + +- [#1337](https://github.com/NibiruChain/nibiru/pull/1337) - fix(ci): fix dockerfile with rocksdb +- [#1276](https://github.com/NibiruChain/nibiru/pull/1276) - feat: add ewma function +- [#1218](https://github.com/NibiruChain/nibiru/pull/1218) - ci(release): Publish chaosnet image when tagging a release +- [#1210](https://github.com/NibiruChain/nibiru/pull/1210) - fix(ci): fix docker push workflow + +### Dependencies + +- Bump `technote-space/get-diff-action` from 4 to 6 (#1327) +- Bump `robinraju/release-downloader` from 1.6 to 1.8 (#1326) +- Bump `pozetroninc/github-action-get-latest-release` from 0.6.0 to 0.7.0 (#1325) +- Bump `actions/setup-go` from 3 to 4 (#1324) + +- [#1321](https://github.com/NibiruChain/nibiru/pull/1321) - build(deps): bump github.com/prometheus/client_golang from 1.15.0 to 1.15.1 +- [#1256](https://github.com/NibiruChain/nibiru/pull/1256) - chore(deps): bump github.com/spf13/cobra from 1.6.1 to 1.7.0 +- [#1231](https://github.com/NibiruChain/nibiru/pull/1231) - chore(deps): bump github.com/cosmos/ibc-go/v4 from 4.2.0 to 4.3.0 #1231 +- [#1230](https://github.com/NibiruChain/nibiru/pull/1230) - chore(deps): Bump github.com/holiman/uint256 from 1.2.1 to 1.2.2 +- [#1223](https://github.com/NibiruChain/nibiru/pull/1223) - chore(deps): bump github.com/golang/protobuf from 1.5.2 to 1.5.3 +- [#1222](https://github.com/NibiruChain/nibiru/pull/1222) - chore(deps): bump google.golang.org/protobuf from 1.28.2-0.20220831092852-f930b1dc76e8 to 1.29.0 +- [#1211](https://github.com/NibiruChain/nibiru/pull/1211) - chore(deps): Bump github.com/stretchr/testify from 1.8.1 to 1.8.2 + +- [#1283](https://github.com/NibiruChain/nibiru/pull/1283) - chore(deps): bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0 + +## [v0.19.2](https://github.com/NibiruChain/nibiru/releases/tag/v0.19.2) - 2023-02-24 + +Summary: Changes up to pull request #1208 + +### State Machine Breaking + +- [#1196](https://github.com/NibiruChain/nibiru/pull/1196) - refactor(spot)!: default whitelisted asset and query cli +- [#1195](https://github.com/NibiruChain/nibiru/pull/1195) - feat(perp)!: Add `MultiLiquidation` feature for perps +- [#1194](https://github.com/NibiruChain/nibiru/pull/1194) - fix(oracle): local min voters +- [#1187](https://github.com/NibiruChain/nibiru/pull/1187) - feat(oracle): default vote threshold and min voters +- [#1176](https://github.com/NibiruChain/nibiru/pull/1176) - refactor(spot)!: replace `x/dex` module with `x/spot`. +- [#1173](https://github.com/NibiruChain/nibiru/pull/1173) - refactor(spot)!: replace `x/dex` module with `x/spot`. +- [#1171](https://github.com/NibiruChain/nibiru/pull/1171) - refactor(asset)!: Replace `common.AssetPair` with `asset.Pair`. +- [#1164](https://github.com/NibiruChain/nibiru/pull/1164) - refactor: remove client interface for liquidate msg +- [#1158](https://github.com/NibiruChain/nibiru/pull/1158) - feat(asset-registry)!: Add `AssetRegistry` +- [#1156](https://github.com/NibiruChain/nibiru/pull/1156) - refactor: remove lockup & incentivation module +- [#1154](https://github.com/NibiruChain/nibiru/pull/1154) - refactor(asset-pair)!: refactors `common.AssetPair` as an extension of string +- [#1151](https://github.com/NibiruChain/nibiru/pull/1151) - fix(dex): fix swap calculation for stableswap pools +- [#1131](https://github.com/NibiruChain/nibiru/pull/1131) - fix(oracle): use correct distribution module account + +### Non-breaking/Compatible Improvements + +- [#1205](https://github.com/NibiruChain/nibiru/pull/1205) - test: first testing framework skeleton and example +- [#1203](https://github.com/NibiruChain/nibiru/pull/1203) - ci: make chaosnet pull nibiru image if --build is not specified +- [#1199](https://github.com/NibiruChain/nibiru/pull/1199) - chore(deps): bump golang.org/x/net from 0.4.0 to 0.7.0 +- [#1197](https://github.com/NibiruChain/nibiru/pull/1197) - feat: add fees into events in spot module: `EventPoolExited`, `EventPoolCreated`, `EventAssetsSwapped`. +- [#1197](https://github.com/NibiruChain/nibiru/pull/1197) - refactor(testutil): clean up `x/common/testutil` test setup code +- [#1193](https://github.com/NibiruChain/nibiru/pull/1193) - refactor(oracle): clean up `x/oracle/keeper` tests +- [#1192](https://github.com/NibiruChain/nibiru/pull/1192) - feat: chaosnet docker-compose +- [#1191](https://github.com/NibiruChain/nibiru/pull/1191) - fix(oracle): default whitelisted pairs +- [#1190](https://github.com/NibiruChain/nibiru/pull/1190) - ci(release): fix TM_VERSION not being set on releases +- [#1189](https://github.com/NibiruChain/nibiru/pull/1189) - ci(codecov): add Codecov reporting +- [#1188](https://github.com/NibiruChain/nibiru/pull/1188) - fix(spot): remove A precision and clean up borked logic +- [#1184](https://github.com/NibiruChain/nibiru/pull/1184) - docs(oracle): proto type docs, (2) spec clean-up, and (3) remove panic case +- [#1181](https://github.com/NibiruChain/nibiru/pull/1181) - refactor(oracle): keeper method locations +- [#1180](https://github.com/NibiruChain/nibiru/pull/1180) - refactor(oracle): whitelist refactor +- [#1179](https://github.com/NibiruChain/nibiru/pull/1179) - refactor(oracle): types refactor for validator performance map and whitelist map +- [#1165](https://github.com/NibiruChain/nibiru/pull/1165) - chore(deps): bump cosmos-sdk to [v0.45.12](https://github.com/cosmos/cosmos-sdk/blob/release/v0.45.x/CHANGELOG.md#v04512---2023-01-23) +- [#1161](https://github.com/NibiruChain/nibiru/pull/1161) - refactor: migrate simapp tests to use main app +- [#1160](https://github.com/NibiruChain/nibiru/pull/1160) - feat: generic set +- [#1149](https://github.com/NibiruChain/nibiru/pull/1149) - chore(deps): Bump [github.com/btcsuite/btcd](https://github.com/btcsuite/btcd) from 0.22.1 to 0.22.2 +- [#1146](https://github.com/NibiruChain/nibiru/pull/1146) - fix: local docker-compose network +- [#1145](https://github.com/NibiruChain/nibiru/pull/1145) - chore: add USD quote asset +- [#1144](https://github.com/NibiruChain/nibiru/pull/1144) - ci: release for linux and darwin (arm64 and amd64) +- [#1141](https://github.com/NibiruChain/nibiru/pull/1141) - refactor(oracle): rename variables for readability +- [#1139](https://github.com/NibiruChain/nibiru/pull/1139) - feat: add default oracle whitelisted pairs +- [#1138](https://github.com/NibiruChain/nibiru/pull/1138) - refactor: put Makefile workflows in separate directory +- [#1135](https://github.com/NibiruChain/nibiru/pull/1135) - fix: add genesis oracle prices to localnet +- [#1134](https://github.com/NibiruChain/nibiru/pull/1134) - refactor: remove panics from vpool and spillovers from the perp module. It's now impossible to call functions in x/perp that would panic in vpool. +- [#1127](https://github.com/NibiruChain/nibiru/pull/1127) - refactor: remove unnecessary panics from x/dex and x/stablecoin +- [#1126](https://github.com/NibiruChain/nibiru/pull/1126) - test(oracle): stop the tyrannical behavior of TestFuzz_PickReferencePair +- [#1126](https://github.com/NibiruChain/nibiru/pull/1126) - test(oracle): stop the tyrannical behavior of TestFuzz_PickReferencePair +- [#1126](https://github.com/NibiruChain/nibiru/pull/1126) - refactor(perp): remove unnecessary panics +- [#1089](https://github.com/NibiruChain/nibiru/pull/1089) - refactor(deps): Bump [github.com/holiman/uint256](https://github.com/holiman/uint256) from 1.1.1 to 1.2.1 (syntax changes) +- [#1032](https://github.com/NibiruChain/nibiru/pull/1107) - ci: Create e2e wasm contract test + +## [v0.16.3] - 2022-12-28 + +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v0.16.3)] + [[Commits](https://github.com/NibiruChain/nibiru/commits/v0.16.3)] + +### Features + +- [#1115](https://github.com/NibiruChain/nibiru/pull/1115) - feat: improve single asset join calculation +- [#1117](https://github.com/NibiruChain/nibiru/pull/1117) - feat: wire multi-liquidate transaction +- [#1120](https://github.com/NibiruChain/nibiru/pull/1120) - feat: replace pricefeed with oracle + +### Bug Fixes + +- [#1113](https://github.com/NibiruChain/nibiru/pull/1113) - fix: fix quick simulation issue +- [#1114](https://github.com/NibiruChain/nibiru/pull/1114) - fix(dex): fix single asset join +- [#1116](https://github.com/NibiruChain/nibiru/pull/1116) - fix(dex): unfroze pool when LP share supply of 0 +- [#1124](https://github.com/NibiruChain/nibiru/pull/1124) - fix(dex): fix unexpected panic in stableswap calcs + +## [v0.16.2] - 2022-12-13 + +- [[Release Link](https://github.com/NibiruChain/nibiru/releases/tag/v0.16.2)] + [[Commits](https://github.com/NibiruChain/nibiru/commits/v0.16.2)] + +### Features + +- [#1032](https://github.com/NibiruChain/nibiru/pull/1032) - feeder: add price provide API and bitfinex price source +- [#1038](https://github.com/NibiruChain/nibiru/pull/1038) - feat(dex): add single asset join +- [#1050](https://github.com/NibiruChain/nibiru/pull/1050) - feat(dex): add stableswap pools +- [#1058](https://github.com/NibiruChain/nibiru/pull/1058) - feature: use collections external lib +- [#1082](https://github.com/NibiruChain/nibiru/pull/1082) - feat(vpool): Add gov proposal for editing the sswap invariant of a vpool.. +- [#1092](https://github.com/NibiruChain/nibiru/pull/1092) - refactor(dex)!: revive dex module using intermediate test app +- [#1097](https://github.com/NibiruChain/nibiru/pull/1097) - feat(perp): Track and expose the net size of a pair with a query +- [#1105](https://github.com/NibiruChain/nibiru/pull/1105) - feat(perp): Add (notional) volume to metrics state + +### API Breaking + +- [#1074](https://github.com/NibiruChain/nibiru/pull/1074) - feat(vpool): Add gov proposal for editing the vpool config without changing the reserves. + +### State Machine Breaking + +- [#1102](https://github.com/NibiruChain/nibiru/pull/1102) - refactor(perp)!: replace CumulativePremiumFractions array with single value + +### Breaking Changes + +- [#1074](https://github.com/NibiruChain/nibiru/pull/1074) - feat(vpool): Add gov proposal for editing the vpool config without changing the reserves. + +### Improvements + +- [#1111](https://github.com/NibiruChain/nibiru/pull/1111) - feat(vpool)!: Use flags and certain default values instead of unnamed args for add-genesis-vpool to improve ease of use +- [#1046](https://github.com/NibiruChain/nibiru/pull/1046) - remove: feeder. The price feeder was moved to an external repo. +- [#1015](https://github.com/NibiruChain/nibiru/pull/1015) - feat(dex): throw error when swap output amount is less than 1 +- [#1018](https://github.com/NibiruChain/nibiru/pull/1018) - chore(dex): refactor to match best practice +- [#1024](https://github.com/NibiruChain/nibiru/pull/1024) - refactor(oracle): remove Pair and PairList +- [#1034](https://github.com/NibiruChain/nibiru/pull/1034) - refactor(proto): use proto-typed events x/dex +- [#1035](https://github.com/NibiruChain/nibiru/pull/1035) - refactor(proto): use proto-typed events for epochs +- [#1014](https://github.com/NibiruChain/nibiru/pull/1014) - refactor(oracle): full refactor of EndBlock UpdateExchangeRates() long function +- [#1054](https://github.com/NibiruChain/nibiru/pull/1054) - chore(deps): Bump github.com/cosmos/ibc-go/v3 from 3.3.0 to 3.4.0 +- [#1043](https://github.com/NibiruChain/nibiru/pull/1043) - chore(deps): Bump github.com/spf13/cobra from 1.6.0 to 1.6.1 +- [#1056](https://github.com/NibiruChain/nibiru/pull/1056) - chore(deps): Bump github.com/prometheus/client_golang from 1.13.0 to 1.13.1 +- [#1055](https://github.com/NibiruChain/nibiru/pull/1055) - chore(deps): Bump github.com/spf13/viper from 1.13.0 to 1.14.0 +- [#1061](https://github.com/NibiruChain/nibiru/pull/1061) - feat(cmd): hard-code block time parameters in the Tendermint config +- [#1068](https://github.com/NibiruChain/nibiru/pull/1068) - refactor(vpool)!: Remove ReserveSnapshot from the vpool genesis state since reserves are taken automatically on vpool initialization. +- [#1064](https://github.com/NibiruChain/nibiru/pull/1064) - test(wasm): add test for Cosmwasm +- [#1075](https://github.com/NibiruChain/nibiru/pull/1075) - feat(dex): remove possibility to create multiple pools with the same assets +- [#1080](https://github.com/NibiruChain/nibiru/pull/1080) - feat(perp): Add exchanged notional to the position changed event #1080 +- [#1082](https://github.com/NibiruChain/nibiru/pull/1082) - feat(localnet.sh): Set genesis prices based on real BTC and ETH prices +- [#1086](https://github.com/NibiruChain/nibiru/pull/1086) - refactor(perp)!: Removed unused field, `LiquidationPenalty`, from `PositionChangedEvent` +- [#1093](https://github.com/NibiruChain/nibiru/pull/1093) - simulation(dex): add simulation tests for stableswap pools +- [#1091](https://github.com/NibiruChain/nibiru/pull/1091) - refactor: Use common.Precision instead of 1_000_000 in the codebase +- [#1109](https://github.com/NibiruChain/nibiru/pull/1109) - refactor(vpool)!: Condense swap SwapXForY and SwapYForX events into SwapEvent + +### Bug Fixes + +- [#1100](https://github.com/NibiruChain/nibiru/pull/1100) - fix(oracle): fix flaky oracle test +- [#1110](https://github.com/NibiruChain/nibiru/pull/1110) - fix(dex): fix dex issue on unsorted join pool + +### CI + +- [#1088](https://github.com/NibiruChain/nibiru/pull/1088) - ci: build cross binaries + +## v0.15.0 + +### CI + +- [#785](https://github.com/NibiruChain/nibiru/pull/785) - ci: create simulations job + +### State Machine Breaking + +- [#994](https://github.com/NibiruChain/nibiru/pull/994) - x/oracle refactor to use collections +- [#991](https://github.com/NibiruChain/nibiru/pull/991) - collections refactoring of keys and values +- [#978](https://github.com/NibiruChain/nibiru/pull/978) - x/vpool move state logic to collections +- [#977](https://github.com/NibiruChain/nibiru/pull/977) - x/perp add whitelisted liquidators +- [#960](https://github.com/NibiruChain/nibiru/pull/960) - x/common validate asset pair denoms +- [#952](https://github.com/NibiruChain/nibiru/pull/952) - x/perp move state logic to collections +- [#872](https://github.com/NibiruChain/nibiru/pull/872) - x/perp remove module balances from genesis +- [#878](https://github.com/NibiruChain/nibiru/pull/878) - rename `PremiumFraction` to `FundingRate` +- [#900](https://github.com/NibiruChain/nibiru/pull/900) - refactor x/vpool snapshot state management +- [#904](https://github.com/NibiruChain/nibiru/pull/904) - refactor: change Pool name to VPool in vpool module +- [#894](https://github.com/NibiruChain/nibiru/pull/894) - add the collections package! +- [#897](https://github.com/NibiruChain/nibiru/pull/897) - x/pricefeed - use collections. +- [#933](https://github.com/NibiruChain/nibiru/pull/933) - refactor(perp): remove whitelist and simplify state keys +- [#959](https://github.com/NibiruChain/nibiru/pull/959) - feat(vpool): complete genesis import export + - removed Params from genesis. + - added pair into ReserveSnapshot type. + - added validation of snapshots and snapshots in genesis. +- [#975](https://github.com/NibiruChain/nibiru/pull/975) - fix(perp): funding payment calculations +- [#976](https://github.com/NibiruChain/nibiru/pull/976) - refactor(epochs): refactor to increase readability and some tests + - EpochInfo.CurrentEpoch changed from int64 to uint64. + +### API Breaking + +- [#880](https://github.com/NibiruChain/nibiru/pull/880) - refactor `PostRawPrice` return values +- [#900](https://github.com/NibiruChain/nibiru/pull/900) - fix x/vpool twap calculation to be bounded in time +- [#919](https://github.com/NibiruChain/nibiru/pull/919) - refactor(proto): vpool module files consistency + - MarkPriceChanged renamed to MarkPriceChangedEvent +- [#875](https://github.com/NibiruChain/nibiru/pull/875) - x/perp add MsgMultiLiquidate +- [#979](https://github.com/NibiruChain/nibiru/pull/979) - refactor and clean VPool. + +### Improvements + +- [#1044](https://github.com/NibiruChain/nibiru/pull/1044) - feat(wasm): cosmwasm module integration +- [#858](https://github.com/NibiruChain/nibiru/pull/858) - fix trading limit ratio check; checks in both directions on both quote and base assets +- [#865](https://github.com/NibiruChain/nibiru/pull/865) - refactor(vpool): clean up interface for CmdGetBaseAssetPrice to use add and remove as directions +- [#868](https://github.com/NibiruChain/nibiru/pull/868) - refactor dex integration tests to be independent between them +- [#876](https://github.com/NibiruChain/nibiru/pull/876) - chore(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0 +- [#879](https://github.com/NibiruChain/nibiru/pull/879) - test(perp): liquidate cli test and genesis fix for testutil initGenFiles +- [#889](https://github.com/NibiruChain/nibiru/pull/889) - feat: decouple keeper from servers in pricefeed module +- [#886](https://github.com/NibiruChain/nibiru/pull/886) - feat: decouple keeper from servers in perp module +- [#901](https://github.com/NibiruChain/nibiru/pull/901) - refactor(vpool): remove `GetUnderlyingPrice` method +- [#902](https://github.com/NibiruChain/nibiru/pull/902) - refactor(common): improve usability of `common.AssetPair` +- [#913](https://github.com/NibiruChain/nibiru/pull/913) - chore(epochs): update x/epochs module +- [#911](https://github.com/NibiruChain/nibiru/pull/911) - test(perp): add `MsgOpenPosition` simulation tests +- [#917](https://github.com/NibiruChain/nibiru/pull/917) - refactor(proto): perp module files consistency +- [#920](https://github.com/NibiruChain/nibiru/pull/920) - refactor(proto): pricefeed module files consistency +- [#926](https://github.com/NibiruChain/nibiru/pull/926) - feat: use spot twap for funding rate calculation +- [#932](https://github.com/NibiruChain/nibiru/pull/932) - refactor(perp): rename premium fraction to funding rate +- [#963](https://github.com/NibiruChain/nibiru/pull/963) - test: add collections api tests +- [#971](https://github.com/NibiruChain/nibiru/pull/971) - chore: use upstream 99designs/keyring module +- [#964](https://github.com/NibiruChain/nibiru/pull/964) - test(vpool): refactor flaky vpool cli test +- [#956](https://github.com/NibiruChain/nibiru/pull/956) - test(perp): partial liquidate unit test +- [#981](https://github.com/NibiruChain/nibiru/pull/981) - chore(testutil): clean up x/testutil packages +- [#980](https://github.com/NibiruChain/nibiru/pull/980) - test(perp): add `MsgClosePosition`, `MsgAddMargin`, and `MsgRemoveMargin` simulation tests +- [#987](https://github.com/NibiruChain/nibiru/pull/987) - feat: create a query that directly returns all module accounts without pagination or iteration +- [#982](https://github.com/NibiruChain/nibiru/pull/982) - improvements for pricefeed genesis +- [#989](https://github.com/NibiruChain/nibiru/pull/989) - test(perp): cli test for AddMargin +- [#1001](https://github.com/NibiruChain/nibiru/pull/1001) - chore(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0 +- [#1013](https://github.com/NibiruChain/nibiru/pull/1013) - test(vpool): more calc twap tests and documentation +- [#1012](https://github.com/NibiruChain/nibiru/pull/1012) - test(vpool): make vpool simulation with random parameters + +### Features + +- [#1019](https://github.com/NibiruChain/nibiru/pull/1019) - add fields to the snapshot reserve event +- [#1010](https://github.com/NibiruChain/nibiru/pull/1010) - feeder: initialize oracle feeder core logic +- [#966](https://github.com/NibiruChain/nibiru/pull/966) - collections: add indexed map +- [#852](https://github.com/NibiruChain/nibiru/pull/852) - feat(genesis): add cli command to add pairs at genesis +- [#861](https://github.com/NibiruChain/nibiru/pull/861) - feat: query cumulative funding payments +- [#985](https://github.com/NibiruChain/nibiru/pull/985) - feat: query all active positions for a trader +- [#997](https://github.com/NibiruChain/nibiru/pull/997) - feat: emit `ReserveSnapshotSavedEvent` in vpool EndBlocker +- [#1011](https://github.com/NibiruChain/nibiru/pull/1011) - feat(perp): add DonateToEF cli command +- [#1044](https://github.com/NibiruChain/nibiru/pull/1044) - feat(wasm): cosmwasm module integration + +### Fixes + +- [#1023](https://github.com/NibiruChain/nibiru/pull/1023) - collections: golang compiler bug +- [#1017](https://github.com/NibiruChain/nibiru/pull/1017) - collections: correctly reports value type and key in case of not found errors. +- [#857](https://github.com/NibiruChain/nibiru/pull/857) - x/perp add proper stateless genesis validation checks +- [#874](https://github.com/NibiruChain/nibiru/pull/874) - fix --home issue with unsafe-reset-all command, updating tendermint to v0.34.21 +- [#892](https://github.com/NibiruChain/nibiru/pull/892) - chore: fix localnet script +- [#925](https://github.com/NibiruChain/nibiru/pull/925) - fix(vpool): snapshot iteration +- [#930](https://github.com/NibiruChain/nibiru/pull/930) - fix(vpool): snapshot iteration on mark twap +- [#911](https://github.com/NibiruChain/nibiru/pull/911) - fix(perp): handle issue where no vpool snapshots are found +- [#958](https://github.com/NibiruChain/nibiru/pull/930) - fix(pricefeed): add twap to prices query +- [#961](https://github.com/NibiruChain/nibiru/pull/961) - fix(perp): wire the funding rate query +- [#993](https://github.com/NibiruChain/nibiru/pull/993) - fix(vpool): fluctuation limit check +- [#1000](https://github.com/NibiruChain/nibiru/pull/1000) - chore: bump cosmos-sdk to v0.45.9 to fix ibc bug +- [#1002](https://github.com/NibiruChain/nibiru/pull/1002) - fix: update go.mod dependencies to fix the protocgen script + +## v0.14.0 + +### API Breaking + +- [#830](https://github.com/NibiruChain/nibiru/pull/830) - test(vpool): Make missing fields for 'query vpool all-pools' display as empty strings. + - Improve test coverage of functions used in the query server. + - Added 'pair' field to the `all-pools` to make the prices array easier to digest +- [#878](https://github.com/NibiruChain/nibiru/pull/878) - rename `funding-payments` query to `funding-rate` + +### Improvements + +- [#837](https://github.com/NibiruChain/nibiru/pull/837) - simplify makefile, removing unused module creation and usage of new command to add vpool at genesis +- [#836](https://github.com/NibiruChain/nibiru/pull/836) - refactor(genesis): DRY improvements and functions added to localnet.sh for readability +- [#842](https://github.com/NibiruChain/nibiru/pull/842) - use self-hosted runner +- [#843](https://github.com/NibiruChain/nibiru/pull/843) - add timeout to github actions integration tests +- [#847](https://github.com/NibiruChain/nibiru/pull/847) - add command in localnet to whitelist oracle +- [#848](https://github.com/NibiruChain/nibiru/pull/848) - add check max leverage on add vpool in genesis command + +### Fixes + +- [#850](https://github.com/NibiruChain/nibiru/pull/850) - x/vpool - properly validate vpools at genesis +- [#854](https://github.com/NibiruChain/nibiru/pull/854) - add buildx to the docker release workflow + +### Features + +- [#827](https://github.com/NibiruChain/nibiru/pull/827) - feat(genesis): add cli command to add vpool at genesis +- [#838](https://github.com/NibiruChain/nibiru/pull/838) - feat(genesis): add cli command to whitelist oracles at genesis +- [#846](https://github.com/NibiruChain/nibiru/pull/846) - x/oracle remove reference pair + +## [v0.13.0](https://github.com/NibiruChain/nibiru/releases/tag/v0.13.0) - 2022-08-16 + +## API Breaking + +- [#831](https://github.com/NibiruChain/nibiru/pull/831) - remove modules that are not used in testnet + +### CI + +- [#795](https://github.com/NibiruChain/nibiru/pull/795) - integration tests run when PR is approved +- [#826](https://github.com/NibiruChain/nibiru/pull/826) - create and push docker image on release + +### Improvements + +- [#798](https://github.com/NibiruChain/nibiru/pull/798) - fix integration tests caused by PR #786 +- [#801](https://github.com/NibiruChain/nibiru/pull/801) - remove unused pair constants +- [#788](https://github.com/NibiruChain/nibiru/pull/788) - add --overwrite flag to the nibid init call of localnet.sh +- [#804](https://github.com/NibiruChain/nibiru/pull/804) - bump ibc-go to v3.1.1 +- [#817](https://github.com/NibiruChain/nibiru/pull/817) - Make post prices transactions gasless for whitelisted oracles +- [#818](https://github.com/NibiruChain/nibiru/pull/818) - fix(localnet.sh): add max leverage to vpools in genesis to fix open-position +- [#819](https://github.com/NibiruChain/nibiru/pull/819) - add golangci-linter using docker in Makefile +- [#835](https://github.com/NibiruChain/nibiru/pull/835) - x/oracle cleanup code + +### Features + +- [#839](https://github.com/NibiruChain/nibiru/pull/839) - x/oracle rewarding +- [#791](https://github.com/NibiruChain/nibiru/pull/791) Add the x/oracle module +- [#811](https://github.com/NibiruChain/nibiru/pull/811) Return the index twap in `QueryPrice` cmd +- [#813](https://github.com/NibiruChain/nibiru/pull/813) - (vpool): Expose mark price, mark TWAP, index price, and k (swap invariant) in the all-pools query +- [#816](https://github.com/NibiruChain/nibiru/pull/816) - Remove tobin tax from x/oracle +- [#810](https://github.com/NibiruChain/nibiru/pull/810) - feat(x/perp): expose 'marginRatioIndex' and block number on QueryPosition +- [#832](https://github.com/NibiruChain/nibiru/pull/832) - x/oracle app wiring + +### Documentation + +- [#814](https://github.com/NibiruChain/nibiru/pull/814) - docs(perp): Added events specification for the perp module. + +## [v0.12.1](https://github.com/NibiruChain/nibiru/releases/tag/v0.12.1) - 2022-08-04 + +- [#796](https://github.com/NibiruChain/nibiru/pull/796) - fix bug that caused that epochKeeper was nil when running epoch hook from Perp module +- [#793](https://github.com/NibiruChain/nibiru/pull/793) - add a vpool parameter to limit leverage in open position + +## [v0.12.0](https://github.com/NibiruChain/nibiru/releases/tag/v0.12.0) - 2022-08-03 + +### Improvements + +- [#775](https://github.com/NibiruChain/nibiru/pull/775) - bump google.golang.org/protobuf from 1.28.0 to 1.28.1 +- [#768](https://github.com/NibiruChain/nibiru/pull/768) - add simulation tests to make file +- [#767](https://github.com/NibiruChain/nibiru/pull/767) - add fluctuation limit checks on `OpenPosition`. +- [#786](https://github.com/NibiruChain/nibiru/pull/786) - add genesis params in localnet script. +- [#770](https://github.com/NibiruChain/nibiru/pull/770) - Return err in case of zero time elapsed and zero snapshots on `GetCurrentTWAP` func. If zero time has elapsed, and snapshots exists, return the instantaneous average. + +### Bug Fixes + +- [#766](https://github.com/NibiruChain/nibiru/pull/766) - Fixed margin ratio calculation for trader position. +- [#776](https://github.com/NibiruChain/nibiru/pull/776) - Fix a bug where the user could open infinite leverage positions +- [#779](https://github.com/NibiruChain/nibiru/pull/779) - Fix issue with released tokens being invalid in `ExitPool` + +### Testing + +- [#782](https://github.com/NibiruChain/nibiru/pull/782) - replace GitHub test workflows to use make commands +- [#784](https://github.com/NibiruChain/nibiru/pull/784) - fix runsim +- [#783](https://github.com/NibiruChain/nibiru/pull/783) - sanitise inputs for msg swap simulations + +## [v0.11.0](https://github.com/NibiruChain/nibiru/releases/tag/v0.11.0) - 2022-07-29 + +### Documentation + +- [#701](https://github.com/NibiruChain/nibiru/pull/701) Add release process guide + +### Improvements + +- [#715](https://github.com/NibiruChain/nibiru/pull/715) - remove redundant perp.Keeper.SetPosition parameters +- [#718](https://github.com/NibiruChain/nibiru/pull/718) - add guard clauses on OpenPosition (leverage and quote amount != 0) +- [#728](https://github.com/NibiruChain/nibiru/pull/728) - add dependabot file into the project. +- [#723](https://github.com/NibiruChain/nibiru/pull/723) - refactor perp keeper's `RemoveMargin` method +- [#730](https://github.com/NibiruChain/nibiru/pull/730) - update localnet script. +- [#736](https://github.com/NibiruChain/nibiru/pull/736) - Bumps [github.com/spf13/cast](https://github.com/spf13/cast) from 1.4.1 to 1.5.0 +- [#735](https://github.com/NibiruChain/nibiru/pull/735) - Bump github.com/spf13/cobra from 1.4.0 to 1.5.0 +- [#729](https://github.com/NibiruChain/nibiru/pull/729) - move maintenance margin to the vpool module +- [#741](https://github.com/NibiruChain/nibiru/pull/741) - remove unused code and refactored variable names. +- [#742](https://github.com/NibiruChain/nibiru/pull/742) - Vpools are not tradeable if they have invalid oracle prices. +- [#739](https://github.com/NibiruChain/nibiru/pull/739) - Bump github.com/spf13/viper from 1.11.0 to 1.12.0 + +### API Breaking + +- [#721](https://github.com/NibiruChain/nibiru/pull/721) - Updated proto property names to adhere to standard snake_casing and added Unlock REST endpoint +- [#724](https://github.com/NibiruChain/nibiru/pull/724) - Add position fields in `ClosePositionResponse`. +- [#737](https://github.com/NibiruChain/nibiru/pull/737) - Renamed from property to avoid python name clash + +### State Machine Breaking + +- [#733](https://github.com/NibiruChain/nibiru/pull/733) - Bump github.com/cosmos/ibc-go/v3 from 3.0.0 to 3.1.0 +- [#741](https://github.com/NibiruChain/nibiru/pull/741) - Rename `epoch_identifier` param to `funding_rate_interval`. +- [#745](https://github.com/NibiruChain/nibiru/pull/745) - Updated pricefeed twap calc to use bounded time + +### Bug Fixes + +- [#746](https://github.com/NibiruChain/nibiru/pull/746) - Pin cosmos-sdk version to v0.45 for proto generation. + +## [v0.10.0](https://github.com/NibiruChain/nibiru/releases/tag/v0.10.0) - 2022-07-18 + +### Improvements + +- [#705](https://github.com/NibiruChain/nibiru/pull/705) Refactor PerpKeeper's `AddMargin` method to accept individual fields instead of the entire Msg object. + +### API Breaking + +- [#709](https://github.com/NibiruChain/nibiru/pull/709) Add fields to `OpenPosition` response. +- [#707](https://github.com/NibiruChain/nibiru/pull/707) Add fluctuation limit checks in vpool methods. +- [#712](https://github.com/NibiruChain/nibiru/pull/712) Add funding rate calculation and `FundingRateChangedEvent`. + +### Upgrades + +- [#725](https://github.com/NibiruChain/nibiru/pull/725) Add governance handler for creating new virtual pools. +- [#702](https://github.com/NibiruChain/nibiru/pull/702) Add upgrade handler for v0.10.0. + +## [v0.9.2](https://github.com/NibiruChain/nibiru/releases/tag/v0.9.2) - 2022-07-11 + +### Improvements + +- [#686](https://github.com/NibiruChain/nibiru/pull/686) Add changelog enforcer to github actions. +- [#681](https://github.com/NibiruChain/nibiru/pull/681) Remove automatic release and leave integration tests when merge into master. +- [#684](https://github.com/NibiruChain/nibiru/pull/684) Reorganize PerpKeeper methods. +- [#690](https://github.com/NibiruChain/nibiru/pull/690) Call `closePositionEntirely` from `ClosePosition`. +- [#689](https://github.com/NibiruChain/nibiru/pull/689) Apply funding rate calculation 48 times per day. + +### API Breaking + +- [#687](https://github.com/NibiruChain/nibiru/pull/687) Emit `PositionChangedEvent` upon changing margin. +- [#685](https://github.com/NibiruChain/nibiru/pull/685) Represent `PositionChangedEvent` bad debt as Coin. +- [#697](https://github.com/NibiruChain/nibiru/pull/697) Rename pricefeed keeper methods. +- [#689](https://github.com/NibiruChain/nibiru/pull/689) Change liquidation params to 2.5% liquidation fee ratio and 25% partial liquidation ratio. + +### Testing + +- [#695](https://github.com/NibiruChain/nibiru/pull/695) Add `OpenPosition` integration tests. +- [#692](https://github.com/NibiruChain/nibiru/pull/692) Add test coverage for Perp MsgServer methods. diff --git a/LICENSE.md b/LICENSE.md index 1705e0cd5..6caf2fb25 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2020 Nibiru Labs, Inc. + Copyright 2024 MTRX Services Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -199,9 +199,3 @@ 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. -$10-worth New Year Gift for You -Make your 2022 productive -with LINER Premium - -Dismiss -Open the gift diff --git a/README.md b/README.md index cf7328079..4b1b13efc 100644 --- a/README.md +++ b/README.md @@ -3,63 +3,52 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/NibiruChain/nibiru.svg)](https://pkg.go.dev/github.com/NibiruChain/nibiru) [![Nibiru Test workflow][badge-go-linter]][workflow-go-linter] [![Nibiru Test workflow][badge-go-releaser]][workflow-go-releaser] -[![GitHub][license-badge]](https://github.com/NibiruChain/nibiru/blob/master/LICENSE.md) +[![GitHub][license-badge]](https://github.com/NibiruChain/nibiru/blob/main/LICENSE.md) [![Discord Badge](https://dcbadge.vercel.app/api/server/nibirufi?style=flat)](https://discord.gg/nibirufi) -**Nibiru Chain** is a proof-of-stake blockchain that unifies leveraged derivatives trading, spot trading, staking, and bonded liquidity provision into a seamless user experience, enabling users of over 40 blockchains to trade with leverage using a suite of composable decentralized applications. - -## Components of Nibiru - -- **CosmWasm Smart Contracts**: Rust-based, WebAssembly (WASM) smart contracts built for the Cosmos Ecosystem. See our [CosmWasm sandbox monorepo (cw-nibiru)](https://github.com/NibiruChain/cw-nibiru/tree/main) for the protocol's core smart contracts. -- **Nibi-Perps**: A perpetual futures exchange where users can take leveraged exposure and trade on a plethora of assets — completely on-chain, completely non-custodially, and with minimal gas fees. -- **Oracle Module**: Nibiru accurately prices assets using a native, system of decentralized oracles, and communicates with other Cosmos layer-1 chains using the Inter-Blockchain Communication (IBC) (opens new window)protocol. -- **Nibi-Swap**: An automated market maker protocol for multichain assets. This application gives users access to swaps, pools, and bonded liquidity gauges. - -## Modules - -| Module | Description | -| --- | --- | -| [common][code-x-common] | Holds helper and utility functions to be utilized by other `x/` modules. | -| [epochs][code-x-epochs] | Often in the SDK, we would like to run certain code every-so often. The purpose of `epochs` module is to allow other modules to set that they would like to be signaled once every period. So another module can specify it wants to execute code once a week, starting at UTC-time = x. `epochs` creates a generalized epoch interface to other modules so that they can easily be signalled upon such events. | -| [inflation][code-x-inflation] | Implements the [tokenomics](https://nibiru.fi/docs/learn/tokenomics.html) for Nibiru. | -| [oracle][code-x-oracle] | Handles the posting of an up-to-date and accurate feed of exchange rates from the validators. | -| [perp][code-x-perp] | Powers the Nibi-Perps exchange. This module enables traders to open long and short leveraged positions and houses all of the PnL calculation and liquidation logic. | -| [spot][code-x-spot] | Responsible for creating, joining, and exiting liquidity pools. It also allows users to swap between two assets in an existing pool. It's a fully functional AMM. | -| [wasm][code-x-wasm] | Implements the execution environment for [WebAssembly (WASM) smart contracts](https://book.cosmwasm.com/). | - -[code-x-common]: https://github.com/NibiruChain/nibiru/tree/master/x/common -[code-x-epochs]: https://github.com/NibiruChain/nibiru/tree/master/x/epochs -[code-x-inflation]: https://github.com/NibiruChain/nibiru/tree/master/x/inflation -[code-x-oracle]: https://github.com/NibiruChain/nibiru/tree/master/x/oracle -[code-x-perp]: https://github.com/NibiruChain/nibiru/tree/master/x/perp -[code-x-spot]: https://github.com/NibiruChain/nibiru/tree/master/x/spot -[code-x-wasm]: https://github.com/NibiruChain/nibiru/tree/master/x/wasm - -Nibiru is built with the [Cosmos-SDK][cosmos-sdk-repo] on [Tendermint Core](https://tendermint.com/core/) consensus, accurately prices assets using a system of decentralized oracles, and communicates with other Cosmos layer-1 chains using the [Inter-Blockchain Communication (IBC)](https://github.com/cosmos/ibc) protocol. +**Nibiru Chain** is a breakthrough Layer 1 blockchain and smart contract ecosystem providing superior throughput, improved security, and a high-performance EVM execution layer. Nibiru aims to be the most developer-friendly and user-friendly smart contract ecosystem, leading the charge toward mainstream Web3 adoption by innovating at each layer of the stack: dApp development, scalable blockchain data indexing, consensus optimizations, a comprehensive developer toolkit, and composability across multiple VMs. ## ⚙️ — Documentation -Conceptual and technical documentation can be found in the [Nibiru docs](https://docs.nibiru.fi). Detailed module-specific documentation is included in the top-level README (`x/module/README.md)`. +- [Docs | Nibiru Chain](https://nibiru.fi/docs/): Conceptual and technical documentation can be found here. +- [Complete Golang reference docs](https://pkg.go.dev/github.com/NibiruChain/nibiru): (`pkg.go.dev`) For the blockchain implementation . +- [Nibiru Modules](https://nibiru.fi/docs/dev/x/): Module-specific documentation ## 💬 — Community -If you have questions or concerns, feel free to connect with a developer or community member in the [Nibiru discord][social-discord]. We also have active communities on Twitter and Telegram. - - +If you have questions or concerns, feel free to connect with a developer or community member in the [Nibiru Discord][social-discord]. We also have active communities on [Twitter][social-twitter] and [Telegram][social-telegram].

Discord Tweet -Telegram +Telegram

----- +## 🧱 — Components of Nibiru + +| Module | Description | +| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Wasm][code-x-wasm] | Implements the execution environment for WebAssembly (WASM) smart contracts. CosmWasm smart contracts are Rust-based, Wasm smart contracts built for enhanced security, performance, and interoperability. See our [CosmWasm sandbox monorepo (nibiru-wasm)](https://github.com/NibiruChain/nibiru-wasm/tree/main) for the protocol's core smart contracts. | +| [EVM][code-x-evm] | Implements Nibiru EVM, which manages an Ethereum Virtual Machine (EVM) state database and enables the execution of Ethereum smart contracts. Nibiru EVM is an extension of "[geth](https://github.com/ethereum/go-ethereum)" along with "web3" and "eth" JSON-RPC methods. | +| [Devgas][code-x-devgas] | The `devgas` module of Nibiru Chain shares contract execution fees with smart contract developers. This aims to increase the adoption of Nibiru by offering CosmWasm smart contract developers a direct source of income based on usage. | +| [Epochs][code-x-epochs] | The `epochs` module allows other modules to set hooks to be called to execute code automatically on a period basis. For example, "once a week, starting at UTC-time = x". `epochs` creates a generalized epoch interface. | +| [Inflation][code-x-inflation] | Implements the [tokenomics](https://nibiru.fi/docs/learn/tokenomics.html) for Nibiru. | +| [Oracle][code-x-oracle] | Nibiru accurately prices assets using a native, system of decentralized oracles, and communicates with other Cosmos layer-1 chains using the Inter-Blockchain Communication (IBC) protocol. Nibi-Oracle handles the voting process for validators that act as oracles by updating data feeds. | +| [Common][code-x-common] | Helper and utility functions to be utilized by other `x/` modules. | -## ⛓️ Installation: Developing on the chain locally +[code-x-common]: https://github.com/NibiruChain/nibiru/tree/main/x/common +[code-x-devgas]: https://nibiru.fi/docs/dev/x/nibiru-chain/devgas.html +[code-x-epochs]: https://github.com/NibiruChain/nibiru/tree/main/x/epochs +[code-x-inflation]: https://github.com/NibiruChain/nibiru/tree/main/x/inflation +[code-x-oracle]: https://github.com/NibiruChain/nibiru/tree/main/x/oracle +[code-x-wasm]: https://nibiru.fi/docs/wasm/ +[code-x-evm]: https://github.com/NibiruChain/nibiru/tree/main/x/evm + +Nibiru is built with the [Cosmos-SDK][cosmos-sdk-repo] on [Tendermint Core](https://tendermint.com/core/) consensus and communicates with other blockchain chains using the [Inter-Blockchain Communication (IBC)](https://github.com/cosmos/ibc) protocol. + +--- + +## ⛓️ — Building: `make` commands Installation instructions for the `nibid` binary can be found in [INSTALL.md](./INSTALL.md). @@ -68,7 +57,7 @@ Recommended minimum specs: - 2CPU, 4GB RAM, 100GB SSD - Unix system: MacOS or Ubuntu 18+ -## Nibid CLI +### Nibid CLI To simply access the `nibid` CLI, run: @@ -78,21 +67,23 @@ make install Usage instructions for the `nibid` CLI are available at [docs.nibiru.fi/dev/cli](https://docs.nibiru.fi/dev/cli/) and the [Nibiru Module Reference](https://docs.nibiru.fi/dev/x/). -## Running a Local Node +### Running a Local Node On a fresh clone of the repo, simply run: + ```bash make localnet -``` -and open another terminal. +``` + +and open another terminal. -## Generate the protobufs +### Generate the protobufs ```bash make proto-gen ``` -## Linter +### Linter We use the [golangci-lint](https://golangci-lint.run/) linter. Install it and run @@ -102,7 +93,7 @@ golangci-lint run at the root directory. You can also install the VSCode or Goland IDE plugins. -## Multiple Nodes +### Multiple Nodes Run the following commands to set up a local network of Docker containers running the chain. @@ -114,19 +105,17 @@ make localnet-start ## License -Licensed under the [MIT License](./LICENSE.md). +Unless a file notes otherwise, it will fall under the [MIT License](./LICENSE.md). [license-badge]: https://img.shields.io/badge/License-MIT-blue.svg [cosmos-sdk-repo]: https://github.com/cosmos/cosmos-sdk -[badge-go-linter]: https://github.com/NibiruChain/nibiru/actions/workflows/golangci-lint.yml/badge.svg?query=branch%3Amaster -[workflow-go-linter]: https://github.com/NibiruChain/nibiru/actions/workflows/golangci-lint.yml?query=branch%3Amaster -[badge-go-releaser]: https://github.com/NibiruChain/nibiru/actions/workflows/goreleaser.yml/badge.svg?query=branch%3Amaster -[workflow-go-releaser]: https://github.com/NibiruChain/nibiru/actions/workflows/goreleaser.yml?query=branch%3Amaster - +[badge-go-linter]: https://github.com/NibiruChain/nibiru/actions/workflows/golangci-lint.yml/badge.svg?query=branch%3Amain +[workflow-go-linter]: https://github.com/NibiruChain/nibiru/actions/workflows/golangci-lint.yml?query=branch%3Amain +[badge-go-releaser]: https://github.com/NibiruChain/nibiru/actions/workflows/goreleaser.yml/badge.svg?query=branch%3Amain +[workflow-go-releaser]: https://github.com/NibiruChain/nibiru/actions/workflows/goreleaser.yml?query=branch%3Amain [social-twitter]: https://twitter.com/NibiruChain [social-discord]: https://discord.gg/nibirufi [social-telegram]: https://t.me/nibiruchain - [discord-badge]: https://img.shields.io/badge/Discord-7289DA?&logo=discord&logoColor=white [twitter-badge]: https://img.shields.io/badge/Twitter-1DA1F2?&logo=twitter&logoColor=white [telegram-badge]: https://img.shields.io/badge/Telegram-2CA5E0?&logo=telegram&logoColor=white @@ -136,7 +125,7 @@ Licensed under the [MIT License](./LICENSE.md). [![version](https://img.shields.io/github/tag/nibiru-labs/nibiru.svg)](https://github.com/NibiruChain/nibiru/releases/latest) -[![Go Report Card](https://goreportcard.com/badge/github.com/NibiruChain/nibiru)](https://goreportcard.com/report/github.com/NibiruChain/nibiru) +[![Go Report Card](https://goreportcard.com/badge/github.com/NibiruChain/nibiru)](https://goreportcard.com/report/github.com/NibiruChain/nibiru) [![API Reference](https://godoc.org/github.com/NibiruChain/nibiru?status.svg)](https://godoc.org/github.com/NibiruChain/nibiru) diff --git a/app/ante.go b/app/ante.go index a4d4a7861..79c2d5adf 100644 --- a/app/ante.go +++ b/app/ante.go @@ -1,85 +1,95 @@ package app import ( - sdkerrors "cosmossdk.io/errors" + "fmt" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/errors" - sdkante "github.com/cosmos/cosmos-sdk/x/auth/ante" ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" - ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" - - "github.com/NibiruChain/nibiru/app/ante" - devgasante "github.com/NibiruChain/nibiru/x/devgas/v1/ante" - devgaskeeper "github.com/NibiruChain/nibiru/x/devgas/v1/keeper" -) -type AnteHandlerOptions struct { - sdkante.HandlerOptions - IBCKeeper *ibckeeper.Keeper - DevGasKeeper *devgaskeeper.Keeper - DevGasBankKeeper devgasante.BankKeeper + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" - TxCounterStoreKey types.StoreKey - WasmConfig *wasmtypes.WasmConfig -} + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/app/evmante" + devgasante "github.com/NibiruChain/nibiru/v2/x/devgas/v1/ante" +) // NewAnteHandler returns and AnteHandler that checks and increments sequence // numbers, checks signatures and account numbers, and deducts fees from the // first signer. -func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) { - if options.AccountKeeper == nil { - return nil, AnteHandlerError("account keeper") - } - if options.BankKeeper == nil { - return nil, AnteHandlerError("bank keeper") - } - if options.SignModeHandler == nil { - return nil, AnteHandlerError("sign mode handler") - } - if options.SigGasConsumer == nil { - options.SigGasConsumer = sdkante.DefaultSigVerificationGasConsumer - } - if options.WasmConfig == nil { - return nil, AnteHandlerError("wasm config") - } - if options.DevGasKeeper == nil { - return nil, AnteHandlerError("devgas keeper") - } - if options.IBCKeeper == nil { - return nil, AnteHandlerError("ibc keeper") - } +func NewAnteHandler( + keepers AppKeepers, + options ante.AnteHandlerOptions, +) sdk.AnteHandler { + return func( + ctx sdk.Context, tx sdk.Tx, sim bool, + ) (newCtx sdk.Context, err error) { + if err := options.ValidateAndClean(); err != nil { + return ctx, err + } - anteDecorators := []sdk.AnteDecorator{ - sdkante.NewSetUpContextDecorator(), - wasmkeeper.NewLimitSimulationGasDecorator(options.WasmConfig.SimulationGasLimit), - wasmkeeper.NewCountTXDecorator(options.TxCounterStoreKey), - sdkante.NewExtensionOptionsDecorator(nil), - sdkante.NewValidateBasicDecorator(), - sdkante.NewTxTimeoutHeightDecorator(), - sdkante.NewValidateMemoDecorator(options.AccountKeeper), - ante.NewPostPriceFixedPriceDecorator(), - ante.AnteDecoratorStakingCommission{}, - sdkante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), - // Replace fee ante from cosmos auth with a custom one. - sdkante.NewDeductFeeDecorator( - options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker), - devgasante.NewDevGasPayoutDecorator( - options.DevGasBankKeeper, options.DevGasKeeper), - // SetPubKeyDecorator must be called before all signature verification decorators - sdkante.NewSetPubKeyDecorator(options.AccountKeeper), - sdkante.NewValidateSigCountDecorator(options.AccountKeeper), - sdkante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), - sdkante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), - sdkante.NewIncrementSequenceDecorator(options.AccountKeeper), - ibcante.NewRedundantRelayDecorator(options.IBCKeeper), - } + var anteHandler sdk.AnteHandler + txWithExtensions, ok := tx.(authante.HasExtensionOptionsTx) + if ok { + opts := txWithExtensions.GetExtensionOptions() + if len(opts) > 0 { + switch typeURL := opts[0].GetTypeUrl(); typeURL { + case "/eth.evm.v1.ExtensionOptionsEthereumTx": + // handle as *evmtypes.MsgEthereumTx + anteHandler = evmante.NewAnteHandlerEVM(options) + default: + return ctx, fmt.Errorf( + "rejecting tx with unsupported extension option: %s", typeURL) + } + + return anteHandler(ctx, tx, sim) + } + } - return sdk.ChainAnteDecorators(anteDecorators...), nil + switch tx.(type) { + case sdk.Tx: + anteHandler = NewAnteHandlerNonEVM(options) + default: + return ctx, fmt.Errorf("invalid tx type (%T) in AnteHandler", tx) + } + return anteHandler(ctx, tx, sim) + } } -func AnteHandlerError(shortDesc string) error { - return sdkerrors.Wrapf(errors.ErrLogic, "%s is required for AnteHandler", shortDesc) +// NewAnteHandlerNonEVM: Default ante handler for non-EVM transactions. +func NewAnteHandlerNonEVM( + opts ante.AnteHandlerOptions, +) sdk.AnteHandler { + return sdk.ChainAnteDecorators( + ante.AnteDecoratorPreventEtheruemTxMsgs{}, // reject MsgEthereumTxs + ante.AnteDecoratorAuthzGuard{}, // disable certain messages in authz grant "generic" + authante.NewSetUpContextDecorator(), + wasmkeeper.NewLimitSimulationGasDecorator(opts.WasmConfig.SimulationGasLimit), + wasmkeeper.NewCountTXDecorator(opts.TxCounterStoreKey), + // TODO: bug(security): Authz is unsafe. Let's include a guard to make + // things safer. + // ticket: https://github.com/NibiruChain/nibiru/issues/1915 + authante.NewExtensionOptionsDecorator(opts.ExtensionOptionChecker), + authante.NewValidateBasicDecorator(), + authante.NewTxTimeoutHeightDecorator(), + authante.NewValidateMemoDecorator(opts.AccountKeeper), + ante.AnteDecoratorEnsureSinglePostPriceMessage{}, + ante.AnteDecoratorStakingCommission{}, + // ----------- Ante Handlers: Gas + authante.NewConsumeGasForTxSizeDecorator(opts.AccountKeeper), + // TODO: spike(security): Does minimum gas price of 0 pose a risk? + // ticket: https://github.com/NibiruChain/nibiru/issues/1916 + authante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.TxFeeChecker), + // ----------- Ante Handlers: devgas + devgasante.NewDevGasPayoutDecorator(opts.DevGasBankKeeper, opts.DevGasKeeper), + // ----------- Ante Handlers: Keys and signatures + // NOTE: SetPubKeyDecorator must be called before all signature verification decorators + authante.NewSetPubKeyDecorator(opts.AccountKeeper), + authante.NewValidateSigCountDecorator(opts.AccountKeeper), + authante.NewSigGasConsumeDecorator(opts.AccountKeeper, opts.SigGasConsumer), + authante.NewSigVerificationDecorator(opts.AccountKeeper, opts.SignModeHandler), + authante.NewIncrementSequenceDecorator(opts.AccountKeeper), + ibcante.NewRedundantRelayDecorator(opts.IBCKeeper), + ante.AnteDecoratorGasWanted{}, + ) } diff --git a/app/ante/auth_grard_test.go b/app/ante/auth_grard_test.go new file mode 100644 index 000000000..838ed804e --- /dev/null +++ b/app/ante/auth_grard_test.go @@ -0,0 +1,138 @@ +package ante_test + +import ( + "time" + + sdkclienttx "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *AnteTestSuite) TestAnteDecoratorAuthzGuard() { + testCases := []struct { + name string + txMsg func() sdk.Msg + wantErr string + }{ + { + name: "sad: authz generic grant with evm message", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + authz.NewGenericAuthorization(sdk.MsgTypeURL(&evm.MsgEthereumTx{})), &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "not allowed", + }, + { + name: "happy: authz generic grant with non evm message", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + authz.NewGenericAuthorization(sdk.MsgTypeURL(&stakingtypes.MsgCreateValidator{})), &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "", + }, + { + name: "happy: authz non generic grant", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + &banktypes.SendAuthorization{}, + &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "", + }, + { + name: "happy: non authz message", + txMsg: func() sdk.Msg { + return &evm.MsgEthereumTx{} + }, + wantErr: "", + }, + { + name: "sad: authz exec with a single evm message", + txMsg: func() sdk.Msg { + msgExec := authz.NewMsgExec( + sdk.AccAddress("nibiuser"), + []sdk.Msg{ + &evm.MsgEthereumTx{}, + }, + ) + return &msgExec + }, + wantErr: "ExtensionOptionsEthereumTx", + }, + { + name: "sad: authz exec with evm message and non evm message", + txMsg: func() sdk.Msg { + msgExec := authz.NewMsgExec( + sdk.AccAddress("nibiuser"), + []sdk.Msg{ + &banktypes.MsgSend{}, + &evm.MsgEthereumTx{}, + }, + ) + return &msgExec + }, + wantErr: "ExtensionOptionsEthereumTx", + }, + { + name: "happy: authz exec without evm messages", + txMsg: func() sdk.Msg { + msgExec := authz.NewMsgExec( + sdk.AccAddress("nibiuser"), + []sdk.Msg{ + &banktypes.MsgSend{}, + }, + ) + return &msgExec + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + anteDec := ante.AnteDecoratorAuthzGuard{} + + encCfg := app.MakeEncodingConfig() + txBuilder, err := sdkclienttx.Factory{}. + WithChainID(s.ctx.ChainID()). + WithTxConfig(encCfg.TxConfig). + BuildUnsignedTx(tc.txMsg()) + s.Require().NoError(err) + + _, err = anteDec.AnteHandle( + deps.Ctx, txBuilder.GetTx(), false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/ante/authz_guard.go b/app/ante/authz_guard.go new file mode 100644 index 000000000..c1cbb8f8d --- /dev/null +++ b/app/ante/authz_guard.go @@ -0,0 +1,67 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package ante + +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/authz" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// AnteDecoratorAuthzGuard filters autz messages +type AnteDecoratorAuthzGuard struct{} + +// AnteHandle rejects "authz grant generic --msg-type '/eth.evm.v1.MsgEthereumTx'" +// Also rejects authz exec tx.json with any MsgEthereumTx inside +func (rmd AnteDecoratorAuthzGuard) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + for _, msg := range tx.GetMsgs() { + // Do not allow grant for MsgEthereumTx + if msgGrant, ok := msg.(*authz.MsgGrant); ok { + if msgGrant.Grant.Authorization == nil { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "grant authorization is missing", + ) + } + authorization, err := msgGrant.Grant.GetAuthorization() + if err != nil { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "failed unmarshaling generic authorization %s", err, + ) + } + if genericAuth, ok := authorization.(*authz.GenericAuthorization); ok { + if genericAuth.MsgTypeURL() == sdk.MsgTypeURL(&evm.MsgEthereumTx{}) { + return ctx, errors.Wrapf( + errortypes.ErrNotSupported, + "authz grant generic for msg type %s is not allowed", + genericAuth.MsgTypeURL(), + ) + } + } + } + // Also reject MsgEthereumTx in exec + if msgExec, ok := msg.(*authz.MsgExec); ok { + msgsInExec, err := msgExec.GetMessages() + if err != nil { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "failed getting exec messages %s", err, + ) + } + for _, msgInExec := range msgsInExec { + if _, ok := msgInExec.(*evm.MsgEthereumTx); ok { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option", + ) + } + } + } + } + return next(ctx, tx, simulate) +} diff --git a/app/ante/commission.go b/app/ante/commission.go index c0de8f0ae..a0fca11da 100644 --- a/app/ante/commission.go +++ b/app/ante/commission.go @@ -1,11 +1,12 @@ package ante import ( + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -func MAX_COMMISSION() sdk.Dec { return sdk.MustNewDecFromStr("0.25") } +func MAX_COMMISSION() sdk.Dec { return math.LegacyMustNewDecFromStr("0.25") } var _ sdk.AnteDecorator = (*AnteDecoratorStakingCommission)(nil) diff --git a/app/ante/commission_test.go b/app/ante/commission_test.go index 7bce01a28..fd4d6ca50 100644 --- a/app/ante/commission_test.go +++ b/app/ante/commission_test.go @@ -1,17 +1,16 @@ package ante_test import ( - "testing" - + "cosmossdk.io/math" sdkclienttx "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/app/ante" - "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" ) func (s *AnteTestSuite) TestAnteDecoratorStakingCommission() { @@ -32,16 +31,16 @@ func (s *AnteTestSuite) TestAnteDecoratorStakingCommission() { valAddr := sdk.ValAddress(testutil.AccAddress()).String() commissionRatePointer := new(sdk.Dec) - *commissionRatePointer = sdk.NewDecWithPrec(10, 2) + *commissionRatePointer = math.LegacyNewDecWithPrec(10, 2) happyMsgs := []sdk.Msg{ &stakingtypes.MsgCreateValidator{ Description: mockDescription, Commission: stakingtypes.CommissionRates{ - Rate: sdk.NewDecWithPrec(6, 2), // 6% - MaxRate: sdk.NewDec(420), - MaxChangeRate: sdk.NewDec(420), + Rate: math.LegacyNewDecWithPrec(6, 2), // 6% + MaxRate: math.LegacyNewDec(420), + MaxChangeRate: math.LegacyNewDec(420), }, - MinSelfDelegation: sdk.NewInt(1), + MinSelfDelegation: math.NewInt(1), DelegatorAddress: testutil.AccAddress().String(), ValidatorAddress: valAddr, Pubkey: &codectypes.Any{}, @@ -58,12 +57,12 @@ func (s *AnteTestSuite) TestAnteDecoratorStakingCommission() { createSadMsgs := func() []sdk.Msg { sadMsgCreateVal := new(stakingtypes.MsgCreateValidator) *sadMsgCreateVal = *(happyMsgs[0]).(*stakingtypes.MsgCreateValidator) - sadMsgCreateVal.Commission.Rate = sdk.NewDecWithPrec(26, 2) + sadMsgCreateVal.Commission.Rate = math.LegacyNewDecWithPrec(26, 2) sadMsgEditVal := new(stakingtypes.MsgEditValidator) *sadMsgEditVal = *(happyMsgs[1]).(*stakingtypes.MsgEditValidator) newCommissionRate := new(sdk.Dec) - *newCommissionRate = sdk.NewDecWithPrec(26, 2) + *newCommissionRate = math.LegacyNewDecWithPrec(26, 2) sadMsgEditVal.CommissionRate = newCommissionRate return []sdk.Msg{ @@ -108,10 +107,10 @@ func (s *AnteTestSuite) TestAnteDecoratorStakingCommission() { wantErr: ante.ErrMaxValidatorCommission.Error(), }, } { - s.T().Run(tc.name, func(t *testing.T) { + s.Run(tc.name, func() { txGasCoins := sdk.NewCoins( - sdk.NewCoin("unibi", sdk.NewInt(1_000)), - sdk.NewCoin("utoken", sdk.NewInt(500)), + sdk.NewCoin("unibi", math.NewInt(1_000)), + sdk.NewCoin("utoken", math.NewInt(500)), ) encCfg := app.MakeEncodingConfig() diff --git a/app/ante/fixed_gas.go b/app/ante/fixed_gas.go index d1f4ca7ad..fbb416d43 100644 --- a/app/ante/fixed_gas.go +++ b/app/ante/fixed_gas.go @@ -4,22 +4,19 @@ import ( sdkerrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - oracletypes "github.com/NibiruChain/nibiru/x/oracle/types" + oracletypes "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) const OracleMessageGas = 500 -var _ sdk.AnteDecorator = EnsureSinglePostPriceMessageDecorator{} +var _ sdk.AnteDecorator = AnteDecoratorEnsureSinglePostPriceMessage{} -// EnsureSinglePostPriceMessageDecorator ensures that there is only one oracle vote message in the transaction -// and sets the gas meter to a fixed value. -type EnsureSinglePostPriceMessageDecorator struct{} +// AnteDecoratorEnsureSinglePostPriceMessage ensures that there is only one +// oracle vote message in the transaction and sets the gas meter to a fixed +// value. +type AnteDecoratorEnsureSinglePostPriceMessage struct{} -func NewPostPriceFixedPriceDecorator() EnsureSinglePostPriceMessageDecorator { - return EnsureSinglePostPriceMessageDecorator{} -} - -func (gd EnsureSinglePostPriceMessageDecorator) AnteHandle( +func (gd AnteDecoratorEnsureSinglePostPriceMessage) AnteHandle( ctx sdk.Context, tx sdk.Tx, simulate bool, diff --git a/app/ante/fixed_gas_test.go b/app/ante/fixed_gas_test.go index 30d9f0bef..64f4c6e8c 100644 --- a/app/ante/fixed_gas_test.go +++ b/app/ante/fixed_gas_test.go @@ -6,17 +6,18 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdkioerrors "cosmossdk.io/errors" + "cosmossdk.io/math" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/signing" - "github.com/cosmos/cosmos-sdk/x/bank/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/app/ante" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - oracletypes "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + oracletypes "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { @@ -61,10 +62,10 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { Feeder: addr.String(), Validator: addr.String(), }, - &types.MsgSend{ + &bank.MsgSend{ FromAddress: addr.String(), ToAddress: addr.String(), - Amount: sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 100)), + Amount: sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 100)), }, }, expectedGas: 1042, @@ -73,10 +74,10 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { { name: "Two messages in a transaction, one of them is an oracle vote message should fail (with MsgAggregateExchangeRatePrevote) permutation 2", messages: []sdk.Msg{ - &types.MsgSend{ + &bank.MsgSend{ FromAddress: addr.String(), ToAddress: addr.String(), - Amount: sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 100)), + Amount: sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 100)), }, &oracletypes.MsgAggregateExchangeRatePrevote{ Hash: "", @@ -96,10 +97,10 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { Feeder: addr.String(), Validator: addr.String(), }, - &types.MsgSend{ + &bank.MsgSend{ FromAddress: addr.String(), ToAddress: addr.String(), - Amount: sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 100)), + Amount: sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 100)), }, }, expectedGas: 1042, @@ -108,10 +109,10 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { { name: "Two messages in a transaction, one of them is an oracle vote message should fail (with MsgAggregateExchangeRateVote) permutation 2", messages: []sdk.Msg{ - &types.MsgSend{ + &bank.MsgSend{ FromAddress: addr.String(), ToAddress: addr.String(), - Amount: sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 100)), + Amount: sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 100)), }, &oracletypes.MsgAggregateExchangeRateVote{ Salt: "dummySalt", @@ -168,10 +169,10 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { Feeder: addr.String(), Validator: addr.String(), }, - &types.MsgSend{ + &bank.MsgSend{ FromAddress: addr.String(), ToAddress: addr.String(), - Amount: sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 100)), + Amount: sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 100)), }, &oracletypes.MsgAggregateExchangeRatePrevote{ Hash: "", @@ -185,30 +186,30 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { { name: "Other two messages", messages: []sdk.Msg{ - &types.MsgSend{ + &bank.MsgSend{ FromAddress: addr.String(), ToAddress: addr.String(), - Amount: sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 100)), + Amount: sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 100)), }, - &types.MsgSend{ + &bank.MsgSend{ FromAddress: addr.String(), ToAddress: addr.String(), - Amount: sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 200)), + Amount: sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 200)), }, }, - expectedGas: 62288, + expectedGas: 67193, expectedErr: nil, }, } for _, tc := range tests { tc := tc - suite.T().Run(tc.name, func(t *testing.T) { + suite.Run(tc.name, func() { suite.SetupTest() // setup suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // msg and signatures - feeAmount := sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 150)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 150)) gasLimit := testdata.NewTestGasLimit() suite.txBuilder.SetFeeAmount(feeAmount) suite.txBuilder.SetGasLimit(gasLimit) @@ -224,7 +225,7 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { err = testapp.FundAccount( suite.app.BankKeeper, suite.ctx, addr, - sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 1000)), + sdk.NewCoins(sdk.NewInt64Coin(appconst.BondDenom, 1000)), ) suite.Require().NoError(err) @@ -239,8 +240,8 @@ func (suite *AnteTestSuite) TestOraclePostPriceTransactionsHaveFixedPrice() { } else { suite.NoError(err) } - want := sdk.NewInt(int64(tc.expectedGas)) - got := sdk.NewInt(int64(suite.ctx.GasMeter().GasConsumed())) + want := math.NewInt(int64(tc.expectedGas)) + got := math.NewInt(int64(suite.ctx.GasMeter().GasConsumed())) suite.Equal(want.String(), got.String()) }) } diff --git a/app/ante/gas.go b/app/ante/gas.go index 7b4c2da98..c6559c2a4 100644 --- a/app/ante/gas.go +++ b/app/ante/gas.go @@ -35,8 +35,11 @@ func (g *fixedGasMeter) GasRemaining() storetypes.Gas { return g.consumed } +// ConsumeGas is a no-op because the fixed gas meter stays fixed. func (g *fixedGasMeter) ConsumeGas(types.Gas, string) {} -func (g *fixedGasMeter) RefundGas(types.Gas, string) {} + +// RefundGas is a no-op because the fixed gas meter stays fixed. +func (g *fixedGasMeter) RefundGas(types.Gas, string) {} func (g *fixedGasMeter) IsPastLimit() bool { return false diff --git a/app/ante/gas_wanted.go b/app/ante/gas_wanted.go new file mode 100644 index 000000000..01bc88fa3 --- /dev/null +++ b/app/ante/gas_wanted.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package ante + +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// AnteDecoratorGasWanted keeps track of the gasWanted amount on the current block in +// transient store for BaseFee calculation. +type AnteDecoratorGasWanted struct{} + +func (gwd AnteDecoratorGasWanted) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return next(ctx, tx, simulate) + } + + gasWanted := feeTx.GetGas() + // return error if the tx gas is greater than the block limit (max gas) + blockGasLimit := eth.BlockGasLimit(ctx) + if gasWanted > blockGasLimit { + return ctx, errors.Wrapf( + errortypes.ErrOutOfGas, + "tx gas (%d) exceeds block gas limit (%d)", + gasWanted, + blockGasLimit, + ) + } + + return next(ctx, tx, simulate) +} diff --git a/app/ante/gas_wanted_test.go b/app/ante/gas_wanted_test.go new file mode 100644 index 000000000..92b5d1343 --- /dev/null +++ b/app/ante/gas_wanted_test.go @@ -0,0 +1,106 @@ +package ante_test + +import ( + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *AnteTestSuite) TestGasWantedDecorator() { + testCases := []struct { + name string + ctxSetup func(deps *evmtest.TestDeps) + txSetup func(deps *evmtest.TestDeps) sdk.Tx + wantErr string + }{ + { + name: "happy: non fee tx type", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return evmtest.HappyCreateContractTx(deps) + }, + wantErr: "", + }, + { + name: "happy: tx without gas, block gas limit 1000", + ctxSetup: func(deps *evmtest.TestDeps) { + cp := &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{MaxGas: 1000}, + } + deps.Ctx = deps.Ctx.WithConsensusParams(cp) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return legacytx.StdTx{ + Msgs: []sdk.Msg{ + evmtest.HappyCreateContractTx(deps), + }, + } + }, + wantErr: "", + }, + { + name: "happy: tx with gas wanted 500, block gas limit 1000", + ctxSetup: func(deps *evmtest.TestDeps) { + cp := &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{MaxGas: 1000}, + } + deps.Ctx = deps.Ctx.WithConsensusParams(cp) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return legacytx.StdTx{ + Msgs: []sdk.Msg{ + evmtest.HappyCreateContractTx(deps), + }, + Fee: legacytx.StdFee{Gas: 500}, + } + }, + wantErr: "", + }, + { + name: "sad: tx with gas wanted 1000, block gas limit 500", + ctxSetup: func(deps *evmtest.TestDeps) { + cp := &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{ + MaxGas: 500, + }, + } + deps.Ctx = deps.Ctx.WithConsensusParams(cp) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return legacytx.StdTx{ + Msgs: []sdk.Msg{ + evmtest.HappyCreateContractTx(deps), + }, + Fee: legacytx.StdFee{Gas: 1000}, + } + }, + wantErr: "exceeds block gas limit", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := ante.AnteDecoratorGasWanted{} + + tx := tc.txSetup(&deps) + s.Require().NoError(stateDB.Commit()) + + deps.Ctx = deps.Ctx.WithIsCheckTx(true) + if tc.ctxSetup != nil { + tc.ctxSetup(&deps) + } + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/ante/handler_opts.go b/app/ante/handler_opts.go new file mode 100644 index 000000000..9c1d88301 --- /dev/null +++ b/app/ante/handler_opts.go @@ -0,0 +1,57 @@ +package ante + +import ( + sdkerrors "cosmossdk.io/errors" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" + sdkante "github.com/cosmos/cosmos-sdk/x/auth/ante" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + + devgasante "github.com/NibiruChain/nibiru/v2/x/devgas/v1/ante" + devgaskeeper "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper" + evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" +) + +type AnteHandlerOptions struct { + sdkante.HandlerOptions + IBCKeeper *ibckeeper.Keeper + DevGasKeeper *devgaskeeper.Keeper + DevGasBankKeeper devgasante.BankKeeper + EvmKeeper *evmkeeper.Keeper + AccountKeeper authkeeper.AccountKeeper + + TxCounterStoreKey types.StoreKey + WasmConfig *wasmtypes.WasmConfig + MaxTxGasWanted uint64 +} + +func (opts *AnteHandlerOptions) ValidateAndClean() error { + if opts.BankKeeper == nil { + return AnteHandlerError("bank keeper") + } + if opts.SignModeHandler == nil { + return AnteHandlerError("sign mode handler") + } + if opts.SigGasConsumer == nil { + opts.SigGasConsumer = sdkante.DefaultSigVerificationGasConsumer + } + if opts.WasmConfig == nil { + return AnteHandlerError("wasm config") + } + if opts.DevGasKeeper == nil { + return AnteHandlerError("devgas keeper") + } + if opts.IBCKeeper == nil { + return AnteHandlerError("ibc keeper") + } + return nil +} + +func AnteHandlerError(shortDesc string) error { + return sdkerrors.Wrapf(errors.ErrLogic, "%s is required for AnteHandler", shortDesc) +} + +type TxFeeChecker func(ctx sdk.Context, feeTx sdk.FeeTx) (sdk.Coins, int64, error) diff --git a/app/ante/reject_ethereum_tx_msgs.go b/app/ante/reject_ethereum_tx_msgs.go new file mode 100644 index 000000000..7b65da403 --- /dev/null +++ b/app/ante/reject_ethereum_tx_msgs.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package ante + +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// AnteDecoratorPreventEtheruemTxMsgs prevents invalid msg types from being executed +type AnteDecoratorPreventEtheruemTxMsgs struct{} + +// AnteHandle rejects messages that requires ethereum-specific authentication. +// For example `MsgEthereumTx` requires fee to be deducted in the antehandler in +// order to perform the refund. +func (rmd AnteDecoratorPreventEtheruemTxMsgs) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + for _, msg := range tx.GetMsgs() { + if _, ok := msg.(*evm.MsgEthereumTx); ok { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option", + ) + } + } + return next(ctx, tx, simulate) +} diff --git a/app/ante/reject_ethereum_tx_msgs_test.go b/app/ante/reject_ethereum_tx_msgs_test.go new file mode 100644 index 000000000..e51ab129b --- /dev/null +++ b/app/ante/reject_ethereum_tx_msgs_test.go @@ -0,0 +1,46 @@ +package ante_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *AnteTestSuite) TestAnteDecoratorPreventEtheruemTxMsgs() { + testCases := []struct { + name string + txSetup func(deps *evmtest.TestDeps) sdk.Tx + wantErr string + }{ + { + name: "sad: evm message", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return evmtest.HappyTransferTx(deps, 0) + }, + wantErr: "invalid type", + }, + { + name: "happy: non evm message", + txSetup: evmtest.NonEvmMsgTx, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + anteDec := ante.AnteDecoratorPreventEtheruemTxMsgs{} + tx := tc.txSetup(&deps) + + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/ante/testutil_test.go b/app/ante/testutil_test.go index 9e6d50edc..bc47bd0f0 100644 --- a/app/ante/testutil_test.go +++ b/app/ante/testutil_test.go @@ -18,9 +18,9 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/NibiruChain/nibiru/app" - nibiruante "github.com/NibiruChain/nibiru/app/ante" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/app" + nibiruante "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" ) // AnteTestSuite is a test suite to be used with ante handler tests. @@ -39,7 +39,7 @@ func (suite *AnteTestSuite) SetupTest() { // Set up base app and ctx testapp.EnsureNibiruPrefix() encodingConfig := app.MakeEncodingConfig() - suite.app = testapp.NewNibiruTestApp(app.NewDefaultGenesisState(encodingConfig.Marshaler)) + suite.app = testapp.NewNibiruTestApp(app.NewDefaultGenesisState(encodingConfig.Codec)) chainId := "test-chain-id" ctx := suite.app.NewContext(true, tmproto.Header{ Height: 1, @@ -66,7 +66,7 @@ func (suite *AnteTestSuite) SetupTest() { ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(suite.app.AccountKeeper), - nibiruante.NewPostPriceFixedPriceDecorator(), + nibiruante.AnteDecoratorEnsureSinglePostPriceMessage{}, nibiruante.AnteDecoratorStakingCommission{}, ante.NewConsumeGasForTxSizeDecorator(suite.app.AccountKeeper), ante.NewDeductFeeDecorator(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.FeeGrantKeeper, nil), // Replace fee ante from cosmos auth with a custom one. diff --git a/app/app.go b/app/app.go index e10e1ca3d..992f8e775 100644 --- a/app/app.go +++ b/app/app.go @@ -28,6 +28,7 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/mempool" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" @@ -44,14 +45,17 @@ import ( "github.com/rakyll/statik/fs" "github.com/spf13/cast" - wasmbinding "github.com/NibiruChain/nibiru/wasmbinding" + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/app/wasmext" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" + + // force call init() of the geth tracers + _ "github.com/ethereum/go-ethereum/eth/tracers/native" ) const ( - AccountAddressPrefix = "nibi" - appName = "Nibiru" - BondDenom = "unibi" - DisplayDenom = "NIBI" + appName = "Nibiru" + DisplayDenom = "NIBI" ) var ( @@ -90,7 +94,7 @@ type NibiruApp struct { AppKeepers // embed all module keepers // the module manager - mm *module.Manager + ModuleManager *module.Manager // simulation manager sm *module.SimulationManager @@ -109,22 +113,25 @@ func init() { } // GetWasmOpts build wasm options -func GetWasmOpts(nibiru NibiruApp, appOpts servertypes.AppOptions) []wasmkeeper.Option { +func GetWasmOpts( + nibiru NibiruApp, + appOpts servertypes.AppOptions, + wasmMsgHandlerArgs wasmext.MsgHandlerArgs, +) []wasmkeeper.Option { var wasmOpts []wasmkeeper.Option if cast.ToBool(appOpts.Get("telemetry.enabled")) { wasmOpts = append(wasmOpts, wasmkeeper.WithVMCacheMetrics(prometheus.DefaultRegisterer)) } - // Add the bindings to the app's set of []wasmkeeper.Option. - wasmOpts = append(wasmOpts, wasmbinding.NibiruWasmOptions( + return append(wasmOpts, wasmext.NibiruWasmOptions( nibiru.GRPCQueryRouter(), nibiru.appCodec, - nibiru.SudoKeeper, + wasmMsgHandlerArgs, )...) - - return wasmOpts } +const DefaultMaxTxGasWanted uint64 = 0 + // overrideWasmVariables overrides the wasm variables to: // - allow for larger wasm files func overrideWasmVariables() { @@ -144,10 +151,17 @@ func NewNibiruApp( baseAppOptions ...func(*baseapp.BaseApp), ) *NibiruApp { overrideWasmVariables() - appCodec := encodingConfig.Marshaler + appCodec := encodingConfig.Codec legacyAmino := encodingConfig.Amino interfaceRegistry := encodingConfig.InterfaceRegistry txConfig := encodingConfig.TxConfig + baseAppOptions = append(baseAppOptions, func(app *baseapp.BaseApp) { + mp := mempool.NoOpMempool{} + app.SetMempool(mp) + handler := baseapp.NewDefaultProposalHandler(mp, app) + app.SetPrepareProposal(handler.PrepareProposalHandler()) + app.SetProcessProposal(handler.ProcessProposalHandler()) + }) bApp := baseapp.NewBaseApp( appName, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...) @@ -176,6 +190,8 @@ func NewNibiruApp( skipGenesisInvariants := cast.ToBool( appOpts.Get(crisis.FlagSkipGenesisInvariants)) + app.EvmKeeper.AddPrecompiles(precompile.InitPrecompiles(app.AppKeepers.PublicKeepers)) + app.initModuleManager(encodingConfig, skipGenesisInvariants) app.setupUpgrades() @@ -185,7 +201,7 @@ func NewNibiruApp( // add test gRPC service for testing gRPC queries in isolation testdata.RegisterQueryServer(app.GRPCQueryRouter(), testdata.QueryImpl{}) - app.InitSimulationManager(app.appCodec) + app.initSimulationManager(app.appCodec) // initialize stores app.MountKVStores(keys) @@ -195,29 +211,31 @@ func NewNibiruApp( // initialize BaseApp app.SetInitChainer(app.InitChainer) app.SetBeginBlocker(app.BeginBlocker) - anteHandler, err := NewAnteHandler(AnteHandlerOptions{ + anteHandler := NewAnteHandler(app.AppKeepers, ante.AnteHandlerOptions{ HandlerOptions: authante.HandlerOptions{ - AccountKeeper: app.AccountKeeper, - BankKeeper: app.BankKeeper, - FeegrantKeeper: app.FeeGrantKeeper, - SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), - SigGasConsumer: authante.DefaultSigVerificationGasConsumer, + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + FeegrantKeeper: app.FeeGrantKeeper, + SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), + SigGasConsumer: authante.DefaultSigVerificationGasConsumer, + ExtensionOptionChecker: func(*codectypes.Any) bool { return true }, }, IBCKeeper: app.ibcKeeper, TxCounterStoreKey: keys[wasmtypes.StoreKey], WasmConfig: &wasmConfig, DevGasKeeper: &app.DevGasKeeper, DevGasBankKeeper: app.BankKeeper, + // TODO: feat(evm): enable app/server/config flag for Evm MaxTxGasWanted. + MaxTxGasWanted: DefaultMaxTxGasWanted, + EvmKeeper: app.EvmKeeper, + AccountKeeper: app.AccountKeeper, }) - if err != nil { - panic(fmt.Errorf("failed to create sdk.AnteHandler: %s", err)) - } app.SetAnteHandler(anteHandler) app.SetEndBlocker(app.EndBlocker) if snapshotManager := app.SnapshotManager(); snapshotManager != nil { - if err = snapshotManager.RegisterExtensions( + if err := snapshotManager.RegisterExtensions( wasmkeeper.NewWasmSnapshotter( app.CommitMultiStore(), &app.WasmKeeper, @@ -257,12 +275,12 @@ func (app *NibiruApp) Name() string { return app.BaseApp.Name() } // BeginBlocker application updates every begin block func (app *NibiruApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { - return app.mm.BeginBlock(ctx, req) + return app.ModuleManager.BeginBlock(ctx, req) } // EndBlocker application updates every end block func (app *NibiruApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { - return app.mm.EndBlock(ctx, req) + return app.ModuleManager.EndBlock(ctx, req) } // InitChainer application update at chain initialization @@ -271,8 +289,8 @@ func (app *NibiruApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) ab if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { panic(err) } - app.upgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap()) - return app.mm.InitGenesis(ctx, app.appCodec, genesisState) + app.upgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()) + return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) } // LoadHeight loads a particular height @@ -400,7 +418,7 @@ func (app *NibiruApp) GetBaseApp() *baseapp.BaseApp { } func (app *NibiruApp) GetStakingKeeper() types.StakingKeeper { - return app.stakingKeeper + return app.StakingKeeper } func (app *NibiruApp) GetIBCKeeper() *ibckeeper.Keeper { diff --git a/app/appconst/appconst.go b/app/appconst/appconst.go new file mode 100644 index 000000000..adb263eb7 --- /dev/null +++ b/app/appconst/appconst.go @@ -0,0 +1,101 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package appconst + +import ( + "fmt" + "math/big" + "runtime" + + db "github.com/cometbft/cometbft-db" +) + +const ( + BinaryName = "nibiru" + BondDenom = "unibi" + // AccountAddressPrefix: Bech32 prefix for Nibiru accounts. + AccountAddressPrefix = "nibi" +) + +var ( + DefaultDBBackend db.BackendType = db.PebbleDBBackend + HavePebbleDBBuildTag bool +) + +// Runtime version vars +var ( + AppVersion = "" + GitCommit = "" + BuildDate = "" + + GoVersion = "" + GoArch = "" +) + +func init() { + if len(AppVersion) == 0 { + AppVersion = "dev" + } + + GoVersion = runtime.Version() + GoArch = runtime.GOARCH +} + +func RuntimeVersion() string { + return fmt.Sprintf( + "Version %s (%s)\nCompiled at %s using Go %s (%s)", + AppVersion, + GitCommit, + BuildDate, + GoVersion, + GoArch, + ) +} + +// EIP 155 Chain IDs exported for tests. +const ( + ETH_CHAIN_ID_MAINNET int64 = 6900 + + ETH_CHAIN_ID_TESTNET_1 int64 = 7210 + ETH_CHAIN_ID_TESTNET_2 int64 = 6911 + ETH_CHAIN_ID_TESTNET_3 int64 = 6912 + + ETH_CHAIN_ID_DEVNET_1 int64 = 6920 + ETH_CHAIN_ID_DEVNET_2 int64 = 6921 + ETH_CHAIN_ID_DEVNET_3 int64 = 6922 + + ETH_CHAIN_ID_LOCALNET_0 int64 = 6930 + ETH_CHAIN_ID_LOCALNET_1 int64 = 6931 + ETH_CHAIN_ID_LOCALNET_2 int64 = 6932 + ETH_CHAIN_ID_LOCALNET_3 int64 = 6933 + + ETH_CHAIN_ID_DEFAULT int64 = 6930 +) + +var knownEthChainIDMap = map[string]int64{ + "cataclysm-1": 6900, + + "nibiru-testnet-1": 7210, + "nibiru-testnet-2": 6911, + "nibiru-testnet-3": 6912, + + "nibiru-devnet-1": 6920, + "nibiru-devnet-2": 6921, + "nibiru-devnet-3": 6922, + + "nibiru-localnet-0": 6930, + "nibiru-localnet-1": 6931, + "nibiru-localnet-2": 6932, + "nibiru-localnet-3": 6933, +} + +// GetEthChainID: Maps the given chain ID from the block's `sdk.Context` to an +// EVM Chain ID (`*big.Int`). +func GetEthChainID(ctxChainID string) (ethChainID *big.Int) { + ethChainIdInt, found := knownEthChainIDMap[ctxChainID] + if !found { + ethChainID = big.NewInt(ETH_CHAIN_ID_DEFAULT) + } else { + ethChainID = big.NewInt(ethChainIdInt) + } + return ethChainID +} diff --git a/app/appconst/appconst_test.go b/app/appconst/appconst_test.go new file mode 100644 index 000000000..caa3e5823 --- /dev/null +++ b/app/appconst/appconst_test.go @@ -0,0 +1,50 @@ +package appconst_test + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/app/appconst" +) + +type TestSuite struct { + suite.Suite +} + +func TestSuite_RunAll(t *testing.T) { + suite.Run(t, new(TestSuite)) +} + +func (s *TestSuite) TestGetEthChainID() { + s.Run("mainnet", func() { + s.EqualValues( + big.NewInt(appconst.ETH_CHAIN_ID_MAINNET), + appconst.GetEthChainID("cataclysm-1"), + ) + }) + s.Run("localnet", func() { + s.EqualValues( + big.NewInt(appconst.ETH_CHAIN_ID_LOCALNET_0), + appconst.GetEthChainID("nibiru-localnet-0"), + ) + }) + s.Run("devnet", func() { + want := big.NewInt(appconst.ETH_CHAIN_ID_TESTNET_1) + given := "nibiru-testnet-1" + s.EqualValues(want, appconst.GetEthChainID(given)) + + want = big.NewInt(appconst.ETH_CHAIN_ID_DEVNET_2) + given = "nibiru-devnet-2" + s.EqualValues(want, appconst.GetEthChainID(given)) + }) + s.Run("else", func() { + want := big.NewInt(appconst.ETH_CHAIN_ID_DEFAULT) + for _, given := range []string{ + "foo", "bloop-blap", "not a chain ID", "", "0x12345", + } { + s.EqualValues(want, appconst.GetEthChainID(given)) + } + }) +} diff --git a/app/appconst/consensus_config.go b/app/appconst/consensus_config.go new file mode 100644 index 000000000..4291d2672 --- /dev/null +++ b/app/appconst/consensus_config.go @@ -0,0 +1,30 @@ +package appconst + +import ( + "time" + + tmcfg "github.com/cometbft/cometbft/config" +) + +// NewDefaultTendermintConfig returns a consensus "Config" (CometBFT) with new +// default values for the "consensus" and "db_backend" fields to be enforced upon +// node initialization. See the "nibiru/cmd/nibid/cmd/InitCmd" function for more +// information. +func NewDefaultTendermintConfig() *tmcfg.Config { + cfg := tmcfg.DefaultConfig() + + // Overwrite consensus config + ms := func(n time.Duration) time.Duration { + return n * time.Millisecond + } + cfg.Consensus.TimeoutPropose = ms(3_000) + cfg.Consensus.TimeoutProposeDelta = ms(500) + cfg.Consensus.TimeoutPrevote = ms(1_000) + cfg.Consensus.TimeoutPrevoteDelta = ms(500) + cfg.Consensus.TimeoutPrecommit = ms(1_000) + cfg.Consensus.TimeoutPrecommitDelta = ms(500) + cfg.Consensus.TimeoutCommit = ms(1_000) + + cfg.DBBackend = string(DefaultDBBackend) + return cfg +} diff --git a/app/appconst/pebbledb.go b/app/appconst/pebbledb.go new file mode 100644 index 000000000..1a0bc1164 --- /dev/null +++ b/app/appconst/pebbledb.go @@ -0,0 +1,8 @@ +//go:build pebbledb +// +build pebbledb + +package appconst + +func init() { + HavePebbleDBBuildTag = true +} diff --git a/app/codec/codec.go b/app/codec/codec.go index 9de4fb1d6..a80f74525 100644 --- a/app/codec/codec.go +++ b/app/codec/codec.go @@ -11,7 +11,7 @@ import ( // This is provided for compatibility between protobuf and amino implementations. type EncodingConfig struct { InterfaceRegistry types.InterfaceRegistry - Marshaler codec.Codec + Codec codec.Codec TxConfig client.TxConfig Amino *codec.LegacyAmino } @@ -25,7 +25,7 @@ func MakeEncodingConfig() EncodingConfig { return EncodingConfig{ InterfaceRegistry: interfaceRegistry, - Marshaler: marshaler, + Codec: marshaler, TxConfig: txCfg, Amino: amino, } diff --git a/app/encoding.go b/app/encoding.go index 1f8e27495..5278ffd8d 100644 --- a/app/encoding.go +++ b/app/encoding.go @@ -3,7 +3,7 @@ package app import ( "github.com/cosmos/cosmos-sdk/std" - "github.com/NibiruChain/nibiru/app/codec" + "github.com/NibiruChain/nibiru/v2/app/codec" ) // EncodingConfig specifies the concrete encoding types to use for a given app. diff --git a/app/evmante/evmante_can_transfer.go b/app/evmante/evmante_can_transfer.go new file mode 100644 index 000000000..f47e7f0b1 --- /dev/null +++ b/app/evmante/evmante_can_transfer.go @@ -0,0 +1,85 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "math/big" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// CanTransferDecorator checks if the sender is allowed to transfer funds according to the EVM block +// context rules. +type CanTransferDecorator struct { + *EVMKeeper +} + +// AnteHandle creates an EVM from the message and calls the BlockContext CanTransfer function to +// see if the address can execute the transaction. +func (ctd CanTransferDecorator) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (sdk.Context, error) { + ethCfg := evm.EthereumConfig(ctd.EVMKeeper.EthChainID(ctx)) + signer := gethcore.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight())) + + for _, msg := range tx.GetMsgs() { + msgEthTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return ctx, errors.Wrapf( + sdkerrors.ErrUnknownRequest, + "invalid message type %T, expected %T", msg, (*evm.MsgEthereumTx)(nil), + ) + } + + baseFeeWeiPerGas := evm.NativeToWei(ctd.EVMKeeper.BaseFeeMicronibiPerGas(ctx)) + + evmMsg, err := msgEthTx.AsMessage(signer, baseFeeWeiPerGas) + if err != nil { + return ctx, errors.Wrapf( + err, + "failed to create an ethereum core.Message from signer %T", signer, + ) + } + + if baseFeeWeiPerGas == nil { + return ctx, errors.Wrap( + evm.ErrInvalidBaseFee, + "base fee is nil for this block.", + ) + } + + if msgEthTx.EffectiveGasCapWei(baseFeeWeiPerGas).Cmp(baseFeeWeiPerGas) < 0 { + return ctx, errors.Wrapf( + sdkerrors.ErrInsufficientFee, + "gas fee cap (wei) less than block base fee (wei); (%s < %s)", + evmMsg.GasFeeCap(), baseFeeWeiPerGas, + ) + } + + // check that caller has enough balance to cover asset transfer for **topmost** call + // NOTE: here the gas consumed is from the context with the infinite gas meter + + if evmMsg.Value().Sign() > 0 { + nibiruAddr := eth.EthAddrToNibiruAddr(evmMsg.From()) + balanceNative := ctd.Bank.GetBalance(ctx, nibiruAddr, evm.EVMBankDenom).Amount.BigInt() + balanceWei := evm.NativeToWei(balanceNative) + + if balanceWei.Cmp(evmMsg.Value()) < 0 { + return ctx, errors.Wrapf( + sdkerrors.ErrInsufficientFunds, + "failed to transfer %s wei ( balance=%s )from address %s using the EVM block context transfer function", + evmMsg.Value(), + balanceWei, + evmMsg.From(), + ) + } + } + } + + return next(ctx, tx, simulate) +} diff --git a/app/evmante/evmante_can_transfer_test.go b/app/evmante/evmante_can_transfer_test.go new file mode 100644 index 000000000..cf3408484 --- /dev/null +++ b/app/evmante/evmante_can_transfer_test.go @@ -0,0 +1,110 @@ +package evmante_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +func (s *TestSuite) TestCanTransferDecorator() { + testCases := []struct { + name string + beforeTxSetup func(deps *evmtest.TestDeps, sdb *statedb.StateDB) + txSetup func(deps *evmtest.TestDeps) sdk.FeeTx + wantErr string + }{ + { + name: "happy: signed tx, sufficient funds", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + s.NoError( + testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 100)), + ), + ) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.FeeTx { + txMsg := evmtest.HappyTransferTx(deps, 0) + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + + gethSigner := gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) + err := txMsg.Sign(gethSigner, deps.Sender.KeyringSigner) + s.Require().NoError(err) + + tx, err := txMsg.BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + + return tx + }, + wantErr: "", + }, + { + name: "sad: signed tx, insufficient funds", + txSetup: func(deps *evmtest.TestDeps) sdk.FeeTx { + txMsg := evmtest.HappyTransferTx(deps, 0) + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + + gethSigner := gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) + err := txMsg.Sign(gethSigner, deps.Sender.KeyringSigner) + s.Require().NoError(err) + + tx, err := txMsg.BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + + return tx + }, + wantErr: "insufficient funds", + }, + { + name: "sad: unsigned tx", + txSetup: func(deps *evmtest.TestDeps) sdk.FeeTx { + txMsg := evmtest.HappyTransferTx(deps, 0) + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + + tx, err := txMsg.BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + + return tx + }, + wantErr: "invalid transaction", + }, + { + name: "sad: tx with non evm message", + txSetup: func(deps *evmtest.TestDeps) sdk.FeeTx { + return evmtest.NonEvmMsgTx(deps).(sdk.FeeTx) + }, + wantErr: "invalid message", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := evmante.CanTransferDecorator{deps.App.AppKeepers.EvmKeeper} + tx := tc.txSetup(&deps) + + if tc.beforeTxSetup != nil { + tc.beforeTxSetup(&deps, stateDB) + err := stateDB.Commit() + s.Require().NoError(err) + } + + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/evmante/evmante_emit_event.go b/app/evmante/evmante_emit_event.go new file mode 100644 index 000000000..d9cc579db --- /dev/null +++ b/app/evmante/evmante_emit_event.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "strconv" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// EthEmitEventDecorator emit events in ante handler in case of tx execution failed (out of block gas limit). +type EthEmitEventDecorator struct { + evmKeeper *EVMKeeper +} + +// NewEthEmitEventDecorator creates a new EthEmitEventDecorator +func NewEthEmitEventDecorator(k *EVMKeeper) EthEmitEventDecorator { + return EthEmitEventDecorator{ + evmKeeper: k, + } +} + +// AnteHandle emits some basic events for the eth messages +func (eeed EthEmitEventDecorator) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + // After eth tx passed ante handler, the fee is deducted and nonce increased, + // it shouldn't be ignored by json-rpc. We need to emit some events at the + // very end of ante handler to be indexed by the consensus engine. + txIndex := eeed.evmKeeper.EVMState().BlockTxIndex.GetOr(ctx, 0) + + for i, msg := range tx.GetMsgs() { + msgEthTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return ctx, errorsmod.Wrapf( + sdkerrors.ErrUnknownRequest, + "invalid message type %T, expected %T", + msg, (*evm.MsgEthereumTx)(nil), + ) + } + // Untyped event "pending_ethereum_tx" is emitted for then indexing purposes. + // Tendermint tx_search can only search the untyped events. + // TxHash and TxIndex values are exposed in the ante handler (before the actual tx execution) + // to allow searching for txs which are failed due to "out of block gas limit" error. + ctx.EventManager().EmitEvent( + sdk.NewEvent( + evm.PendingEthereumTxEvent, + sdk.NewAttribute(evm.PendingEthereumTxEventAttrEthHash, msgEthTx.Hash), + sdk.NewAttribute(evm.PendingEthereumTxEventAttrIndex, strconv.FormatUint(txIndex+uint64(i), 10)), + ), + ) + } + return next(ctx, tx, simulate) +} diff --git a/app/evmante/evmante_emit_event_test.go b/app/evmante/evmante_emit_event_test.go new file mode 100644 index 000000000..20ff36f5d --- /dev/null +++ b/app/evmante/evmante_emit_event_test.go @@ -0,0 +1,79 @@ +package evmante_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/x/evm" + + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + tf "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" +) + +func (s *TestSuite) TestEthEmitEventDecorator() { + testCases := []struct { + name string + txSetup func(deps *evmtest.TestDeps) sdk.Tx + wantErr string + }{ + { + name: "sad: non ethereum tx", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return legacytx.StdTx{ + Msgs: []sdk.Msg{ + &tf.MsgMint{}, + }, + } + }, + wantErr: "invalid message", + }, + { + name: "happy: eth tx emitted event", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + tx := evmtest.HappyCreateContractTx(deps) + return tx + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := evmante.NewEthEmitEventDecorator(deps.App.AppKeepers.EvmKeeper) + + tx := tc.txSetup(&deps) + s.Require().NoError(stateDB.Commit()) + + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + events := deps.Ctx.EventManager().Events() + + s.Require().Greater(len(events), 0) + event := events[len(events)-1] + s.Require().Equal(evm.PendingEthereumTxEvent, event.Type) + + // Convert tx to msg to get hash + txMsg, ok := tx.GetMsgs()[0].(*evm.MsgEthereumTx) + s.Require().True(ok) + + // TX hash attr must present + attr, ok := event.GetAttribute(evm.PendingEthereumTxEventAttrEthHash) + s.Require().True(ok, "tx hash attribute not found") + s.Require().Equal(txMsg.Hash, attr.Value) + + // TX index attr must present + attr, ok = event.GetAttribute(evm.PendingEthereumTxEventAttrIndex) + s.Require().True(ok, "tx index attribute not found") + s.Require().Equal("0", attr.Value) + }) + } +} diff --git a/app/evmante/evmante_gas_consume.go b/app/evmante/evmante_gas_consume.go new file mode 100644 index 000000000..01d39668e --- /dev/null +++ b/app/evmante/evmante_gas_consume.go @@ -0,0 +1,174 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "math" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" +) + +// AnteDecEthGasConsume validates enough intrinsic gas for the transaction and +// gas consumption. +type AnteDecEthGasConsume struct { + evmKeeper *EVMKeeper + maxGasWanted uint64 +} + +// NewAnteDecEthGasConsume creates a new EthGasConsumeDecorator +func NewAnteDecEthGasConsume( + k *EVMKeeper, + maxGasWanted uint64, +) AnteDecEthGasConsume { + return AnteDecEthGasConsume{ + evmKeeper: k, + maxGasWanted: maxGasWanted, + } +} + +// AnteHandle validates that the Ethereum tx message has enough to cover +// intrinsic gas (during CheckTx only) and that the sender has enough balance to +// pay for the gas cost. +// +// Intrinsic gas for a transaction is the amount of gas that the transaction uses +// before the transaction is executed. The gas is a constant value plus any cost +// incurred by additional bytes of data supplied with the transaction. +// +// This AnteHandler decorator will fail if: +// - the message is not a MsgEthereumTx +// - sender account cannot be found +// - transaction's gas limit is lower than the intrinsic gas +// - user has neither enough balance nor staking rewards to deduct the transaction fees (gas_limit * gas_price) +// - transaction or block gas meter runs out of gas +// - sets the gas meter limit +// - gas limit is greater than the block gas meter limit +func (anteDec AnteDecEthGasConsume) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (sdk.Context, error) { + gasWanted := uint64(0) + if ctx.IsReCheckTx() { + // Then, the limit for gas consumed was already checked during CheckTx so + // there's no need to verify it again during ReCheckTx + // + // Use new context with gasWanted = 0 + // Otherwise, there's an error on txmempool.postCheck (tendermint) + // that is not bubbled up. Thus, the Tx never runs on DeliverMode + // Error: "gas wanted -1 is negative" + newCtx := ctx.WithGasMeter(eth.NewInfiniteGasMeterWithLimit(gasWanted)) + return next(newCtx, tx, simulate) + } + + var events sdk.Events + + // Use the lowest priority of all the messages as the final one. + minPriority := int64(math.MaxInt64) + baseFeeMicronibiPerGas := anteDec.evmKeeper.BaseFeeMicronibiPerGas(ctx) + + for _, msg := range tx.GetMsgs() { + msgEthTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return ctx, errors.Wrapf( + sdkerrors.ErrUnknownRequest, + "invalid message type %T, expected %T", + msg, (*evm.MsgEthereumTx)(nil), + ) + } + from := msgEthTx.GetFrom() + + txData, err := evm.UnpackTxData(msgEthTx.Data) + if err != nil { + return ctx, errors.Wrap(err, "failed to unpack tx data") + } + + if ctx.IsCheckTx() && anteDec.maxGasWanted != 0 { + // We can't trust the tx gas limit, because we'll refund the unused gas. + if txData.GetGas() > anteDec.maxGasWanted { + gasWanted += anteDec.maxGasWanted + } else { + gasWanted += txData.GetGas() + } + } else { + gasWanted += txData.GetGas() + } + + fees, err := keeper.VerifyFee( + txData, + baseFeeMicronibiPerGas, + ctx.IsCheckTx(), + ) + if err != nil { + return ctx, errors.Wrapf(err, "failed to verify the fees") + } + + if err = anteDec.deductFee(ctx, fees, from); err != nil { + return ctx, err + } + + events = append(events, + sdk.NewEvent( + sdk.EventTypeTx, + sdk.NewAttribute(sdk.AttributeKeyFee, fees.String()), + ), + ) + + priority := evm.GetTxPriority(txData, baseFeeMicronibiPerGas) + + if priority < minPriority { + minPriority = priority + } + } + + ctx.EventManager().EmitEvents(events) + + blockGasLimit := eth.BlockGasLimit(ctx) + + // return error if the tx gas is greater than the block limit (max gas) + + // NOTE: it's important here to use the gas wanted instead of the gas consumed + // from the tx gas pool. The latter only has the value so far since the + // EthSetupContextDecorator, so it will never exceed the block gas limit. + if gasWanted > blockGasLimit { + return ctx, errors.Wrapf( + sdkerrors.ErrOutOfGas, + "tx gas (%d) exceeds block gas limit (%d)", + gasWanted, + blockGasLimit, + ) + } + + // Set tx GasMeter with a limit of GasWanted (i.e. gas limit from the Ethereum tx). + // The gas consumed will be then reset to the gas used by the state transition + // in the EVM. + + // FIXME: use a custom gas configuration that doesn't add any additional gas and only + // takes into account the gas consumed at the end of the EVM transaction. + newCtx := ctx. + WithGasMeter(eth.NewInfiniteGasMeterWithLimit(gasWanted)). + WithPriority(minPriority) + + // we know that we have enough gas on the pool to cover the intrinsic gas + return next(newCtx, tx, simulate) +} + +// deductFee checks if the fee payer has enough funds to pay for the fees and +// deducts them. +func (anteDec AnteDecEthGasConsume) deductFee( + ctx sdk.Context, fees sdk.Coins, feePayer sdk.AccAddress, +) error { + if fees.IsZero() { + return nil + } + + if err := anteDec.evmKeeper.DeductTxCostsFromUserBalance( + ctx, fees, gethcommon.BytesToAddress(feePayer), + ); err != nil { + return errors.Wrapf(err, "failed to deduct transaction costs from user balance") + } + return nil +} diff --git a/app/evmante/evmante_gas_consume_test.go b/app/evmante/evmante_gas_consume_test.go new file mode 100644 index 000000000..3291c3349 --- /dev/null +++ b/app/evmante/evmante_gas_consume_test.go @@ -0,0 +1,83 @@ +package evmante_test + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +func (s *TestSuite) TestAnteDecEthGasConsume() { + testCases := []struct { + name string + beforeTxSetup func(deps *evmtest.TestDeps, sdb *statedb.StateDB) + txSetup func(deps *evmtest.TestDeps) *evm.MsgEthereumTx + wantErr string + maxGasWanted uint64 + gasMeter sdk.GasMeter + }{ + { + name: "happy: sender with funds", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + gasLimit := happyGasLimit() + balance := evm.NativeToWei(new(big.Int).Add(gasLimit, big.NewInt(100))) + sdb.AddBalance(deps.Sender.EthAddr, balance) + }, + txSetup: evmtest.HappyCreateContractTx, + wantErr: "", + gasMeter: eth.NewInfiniteGasMeterWithLimit(happyGasLimit().Uint64()), + maxGasWanted: 0, + }, + { + name: "happy: is recheck tx", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + deps.Ctx = deps.Ctx.WithIsReCheckTx(true) + }, + txSetup: evmtest.HappyCreateContractTx, + gasMeter: eth.NewInfiniteGasMeterWithLimit(0), + wantErr: "", + }, + { + name: "sad: out of gas", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + gasLimit := happyGasLimit() + balance := evm.NativeToWei(new(big.Int).Add(gasLimit, big.NewInt(100))) + sdb.AddBalance(deps.Sender.EthAddr, balance) + }, + txSetup: evmtest.HappyCreateContractTx, + wantErr: "exceeds block gas limit (0)", + gasMeter: eth.NewInfiniteGasMeterWithLimit(0), + maxGasWanted: 0, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := evmante.NewAnteDecEthGasConsume( + deps.App.AppKeepers.EvmKeeper, tc.maxGasWanted, + ) + + tc.beforeTxSetup(&deps, stateDB) + tx := tc.txSetup(&deps) + s.Require().NoError(stateDB.Commit()) + + deps.Ctx = deps.Ctx.WithIsCheckTx(true) + deps.Ctx = deps.Ctx.WithBlockGasMeter(tc.gasMeter) + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/evmante/evmante_handler.go b/app/evmante/evmante_handler.go new file mode 100644 index 000000000..a9c2f7d0f --- /dev/null +++ b/app/evmante/evmante_handler.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/app/ante" +) + +// NewAnteHandlerEVM creates the default ante handler for Ethereum transactions +func NewAnteHandlerEVM( + options ante.AnteHandlerOptions, +) sdk.AnteHandler { + return sdk.ChainAnteDecorators( + // outermost AnteDecorator. SetUpContext must be called first + NewEthSetUpContextDecorator(options.EvmKeeper), + NewMempoolGasPriceDecorator(options.EvmKeeper), + NewEthValidateBasicDecorator(options.EvmKeeper), + NewEthSigVerificationDecorator(options.EvmKeeper), + NewAnteDecVerifyEthAcc(options.EvmKeeper, options.AccountKeeper), + CanTransferDecorator{options.EvmKeeper}, + NewAnteDecEthGasConsume(options.EvmKeeper, options.MaxTxGasWanted), + NewAnteDecEthIncrementSenderSequence(options.EvmKeeper, options.AccountKeeper), + ante.AnteDecoratorGasWanted{}, + // emit eth tx hash and index at the very last ante handler. + NewEthEmitEventDecorator(options.EvmKeeper), + ) +} diff --git a/app/evmante/evmante_handler_test.go b/app/evmante/evmante_handler_test.go new file mode 100644 index 000000000..5ab7e8a1f --- /dev/null +++ b/app/evmante/evmante_handler_test.go @@ -0,0 +1,109 @@ +package evmante_test + +import ( + "math/big" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +func (s *TestSuite) TestAnteHandlerEVM() { + testCases := []struct { + name string + txSetup func(deps *evmtest.TestDeps) sdk.FeeTx + ctxSetup func(deps *evmtest.TestDeps) + beforeTxSetup func(deps *evmtest.TestDeps, sdb *statedb.StateDB) + wantErr string + }{ + { + name: "happy: signed tx, sufficient funds", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + balanceMicronibi := new(big.Int).Add(evmtest.GasLimitCreateContract(), big.NewInt(100)) + sdb.AddBalance( + deps.Sender.EthAddr, + evm.NativeToWei(balanceMicronibi), + ) + }, + ctxSetup: func(deps *evmtest.TestDeps) { + gasPrice := sdk.NewInt64Coin("unibi", 1) + maxGasMicronibi := new(big.Int).Add(evmtest.GasLimitCreateContract(), big.NewInt(100)) + cp := &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{ + MaxGas: evm.NativeToWei(maxGasMicronibi).Int64(), + }, + } + deps.Ctx = deps.Ctx. + WithMinGasPrices( + sdk.NewDecCoins(sdk.NewDecCoinFromCoin(gasPrice)), + ). + WithIsCheckTx(true). + WithConsensusParams(cp) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.FeeTx { + txMsg := evmtest.HappyTransferTx(deps, 0) + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + + gethSigner := gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) + err := txMsg.Sign(gethSigner, deps.Sender.KeyringSigner) + s.Require().NoError(err) + + tx, err := txMsg.BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + + return tx + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + + anteHandlerEVM := evmante.NewAnteHandlerEVM( + ante.AnteHandlerOptions{ + HandlerOptions: authante.HandlerOptions{ + AccountKeeper: deps.App.AccountKeeper, + BankKeeper: deps.App.BankKeeper, + FeegrantKeeper: deps.App.FeeGrantKeeper, + SignModeHandler: deps.EncCfg.TxConfig.SignModeHandler(), + SigGasConsumer: authante.DefaultSigVerificationGasConsumer, + ExtensionOptionChecker: func(*codectypes.Any) bool { return true }, + }, + EvmKeeper: deps.App.EvmKeeper, + AccountKeeper: deps.App.AccountKeeper, + }) + + tx := tc.txSetup(&deps) + + if tc.ctxSetup != nil { + tc.ctxSetup(&deps) + } + if tc.beforeTxSetup != nil { + tc.beforeTxSetup(&deps, stateDB) + err := stateDB.Commit() + s.Require().NoError(err) + } + + _, err := anteHandlerEVM( + deps.Ctx, tx, false, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/evmante/evmante_increment_sender_seq.go b/app/evmante/evmante_increment_sender_seq.go new file mode 100644 index 000000000..88abaf2e3 --- /dev/null +++ b/app/evmante/evmante_increment_sender_seq.go @@ -0,0 +1,78 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// AnteDecEthIncrementSenderSequence increments the sequence of the signers. +type AnteDecEthIncrementSenderSequence struct { + evmKeeper *EVMKeeper + accountKeeper ante.AccountKeeper +} + +// NewAnteDecEthIncrementSenderSequence creates a new EthIncrementSenderSequenceDecorator. +func NewAnteDecEthIncrementSenderSequence(k *EVMKeeper, ak ante.AccountKeeper) AnteDecEthIncrementSenderSequence { + return AnteDecEthIncrementSenderSequence{ + evmKeeper: k, + accountKeeper: ak, + } +} + +// AnteHandle handles incrementing the sequence of the signer (i.e. sender). If the transaction is a +// contract creation, the nonce will be incremented during the transaction execution and not within +// this AnteHandler decorator. +func (issd AnteDecEthIncrementSenderSequence) AnteHandle( + ctx sdk.Context, + tx sdk.Tx, + simulate bool, + next sdk.AnteHandler, +) (sdk.Context, error) { + for _, msg := range tx.GetMsgs() { + msgEthTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return ctx, errors.Wrapf( + sdkerrors.ErrUnknownRequest, + "invalid message type %T, expected %T", msg, (*evm.MsgEthereumTx)(nil), + ) + } + + txData, err := evm.UnpackTxData(msgEthTx.Data) + if err != nil { + return ctx, errors.Wrap(err, "failed to unpack tx data") + } + + // increase sequence of sender + acc := issd.accountKeeper.GetAccount(ctx, msgEthTx.GetFrom()) + if acc == nil { + return ctx, errors.Wrapf( + sdkerrors.ErrUnknownAddress, + "account %s is nil", gethcommon.BytesToAddress(msgEthTx.GetFrom().Bytes()), + ) + } + nonce := acc.GetSequence() + + // we merged the nonce verification to nonce increment, so when tx includes multiple messages + // with same sender, they'll be accepted. + if txData.GetNonce() != nonce { + return ctx, errors.Wrapf( + sdkerrors.ErrInvalidSequence, + "invalid nonce; got %d, expected %d", txData.GetNonce(), nonce, + ) + } + + if err := acc.SetSequence(nonce + 1); err != nil { + return ctx, errors.Wrapf(err, "failed to set sequence to %d", acc.GetSequence()+1) + } + + issd.accountKeeper.SetAccount(ctx, acc) + } + + return next(ctx, tx, simulate) +} diff --git a/app/evmante/evmante_increment_sender_seq_test.go b/app/evmante/evmante_increment_sender_seq_test.go new file mode 100644 index 000000000..b4503e675 --- /dev/null +++ b/app/evmante/evmante_increment_sender_seq_test.go @@ -0,0 +1,93 @@ +package evmante_test + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +func (s *TestSuite) TestAnteDecEthIncrementSenderSequence() { + testCases := []struct { + name string + beforeTxSetup func(deps *evmtest.TestDeps, sdb *statedb.StateDB) + txSetup func(deps *evmtest.TestDeps) sdk.Tx + wantErr string + wantSeq uint64 + }{ + { + name: "happy: single message", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + balance := big.NewInt(100) + sdb.AddBalance(deps.Sender.EthAddr, balance) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return evmtest.HappyTransferTx(deps, 0) + }, + wantErr: "", + wantSeq: 1, + }, + { + name: "happy: two messages", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + balance := big.NewInt(100) + sdb.AddBalance(deps.Sender.EthAddr, balance) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + txMsgOne := evmtest.HappyTransferTx(deps, 0) + txMsgTwo := evmtest.HappyTransferTx(deps, 1) + + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + s.Require().NoError(txBuilder.SetMsgs(txMsgOne, txMsgTwo)) + + tx := txBuilder.GetTx() + return tx + }, + wantErr: "", + wantSeq: 2, + }, + { + name: "sad: account does not exists", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return evmtest.HappyTransferTx(deps, 0) + }, + wantErr: "unknown address", + }, + { + name: "sad: tx with non evm message", + txSetup: evmtest.NonEvmMsgTx, + wantErr: "invalid message", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := evmante.NewAnteDecEthIncrementSenderSequence(deps.App.EvmKeeper, deps.App.AccountKeeper) + + if tc.beforeTxSetup != nil { + tc.beforeTxSetup(&deps, stateDB) + s.Require().NoError(stateDB.Commit()) + } + tx := tc.txSetup(&deps) + + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + + if tc.wantSeq > 0 { + seq := deps.App.AccountKeeper.GetAccount(deps.Ctx, deps.Sender.NibiruAddr).GetSequence() + s.Require().Equal(tc.wantSeq, seq) + } + }) + } +} diff --git a/app/evmante/evmante_mempool_fees.go b/app/evmante/evmante_mempool_fees.go new file mode 100644 index 000000000..16c35d928 --- /dev/null +++ b/app/evmante/evmante_mempool_fees.go @@ -0,0 +1,79 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "cosmossdk.io/errors" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +var _ sdk.AnteDecorator = MempoolGasPriceDecorator{} + +// MempoolGasPriceDecorator will check if the transaction's fee is at least as large +// as the mempool MinGasPrices param. If fee is too low, decorator returns error and tx +// is rejected. This applies to CheckTx only. +// If fee is high enough, then call next AnteHandler +type MempoolGasPriceDecorator struct { + evmKeeper *EVMKeeper +} + +// NewMempoolGasPriceDecorator creates a new MinGasPriceDecorator instance used only for +// Ethereum transactions. +func NewMempoolGasPriceDecorator(k *EVMKeeper) MempoolGasPriceDecorator { + return MempoolGasPriceDecorator{ + evmKeeper: k, + } +} + +// AnteHandle ensures that the effective fee from the transaction is greater than the +// local mempool gas prices, which is defined by the MinGasPrice (parameter) * GasLimit (tx argument). +func (d MempoolGasPriceDecorator) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + // only run on CheckTx + if !ctx.IsCheckTx() && !simulate { + return next(ctx, tx, simulate) + } + + minGasPrice := ctx.MinGasPrices().AmountOf(evm.EVMBankDenom) + baseFeeMicronibi := d.evmKeeper.BaseFeeMicronibiPerGas(ctx) + baseFeeMicronibiDec := math.LegacyNewDecFromBigInt(baseFeeMicronibi) + + // if MinGasPrices is not set, skip the check + if minGasPrice.IsZero() { + return next(ctx, tx, simulate) + } else if minGasPrice.LT(baseFeeMicronibiDec) { + minGasPrice = baseFeeMicronibiDec + } + + for _, msg := range tx.GetMsgs() { + ethTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return ctx, errors.Wrapf( + sdkerrors.ErrUnknownRequest, + "invalid message type %T, expected %T", + msg, (*evm.MsgEthereumTx)(nil), + ) + } + + baseFeeWei := evm.NativeToWei(baseFeeMicronibi) + effectiveGasPriceDec := math.LegacyNewDecFromBigInt( + evm.WeiToNative(ethTx.EffectiveGasPriceWeiPerGas(baseFeeWei)), + ) + if effectiveGasPriceDec.LT(minGasPrice) { + // if sdk.NewDecFromBigInt(effectiveGasPrice).LT(minGasPrice) { + return ctx, errors.Wrapf( + sdkerrors.ErrInsufficientFee, + "provided gas price < minimum local gas price (%s < %s). "+ + "Please increase the priority tip (for EIP-1559 txs) or the gas prices "+ + "(for access list or legacy txs)", + effectiveGasPriceDec, minGasPrice, + ) + } + } + + return next(ctx, tx, simulate) +} diff --git a/app/evmante/evmante_mempool_fees_test.go b/app/evmante/evmante_mempool_fees_test.go new file mode 100644 index 000000000..892bd9e57 --- /dev/null +++ b/app/evmante/evmante_mempool_fees_test.go @@ -0,0 +1,103 @@ +package evmante_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *TestSuite) TestMempoolGasFeeDecorator() { + testCases := []struct { + name string + txSetup func(deps *evmtest.TestDeps) sdk.Tx + ctxSetup func(deps *evmtest.TestDeps) + wantErr string + }{ + { + name: "happy: min gas price is 0", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + tx := evmtest.HappyCreateContractTx(deps) + return tx + }, + wantErr: "", + }, + { + name: "happy: min gas price is not zero, sufficient fee", + ctxSetup: func(deps *evmtest.TestDeps) { + gasPrice := sdk.NewInt64Coin("unibi", 1) + deps.Ctx = deps.Ctx. + WithMinGasPrices( + sdk.NewDecCoins(sdk.NewDecCoinFromCoin(gasPrice)), + ). + WithIsCheckTx(true) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + tx := evmtest.HappyCreateContractTx(deps) + return tx + }, + wantErr: "", + }, + { + name: "sad: insufficient fee", + ctxSetup: func(deps *evmtest.TestDeps) { + gasPrice := sdk.NewInt64Coin("unibi", 2) + deps.Ctx = deps.Ctx. + WithMinGasPrices( + sdk.NewDecCoins(sdk.NewDecCoinFromCoin(gasPrice)), + ). + WithIsCheckTx(true) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + tx := evmtest.HappyCreateContractTx(deps) + return tx + }, + wantErr: "insufficient fee", + }, + { + name: "sad: tx with non evm message", + ctxSetup: func(deps *evmtest.TestDeps) { + gasPrice := sdk.NewInt64Coin("unibi", 1) + deps.Ctx = deps.Ctx. + WithMinGasPrices( + sdk.NewDecCoins(sdk.NewDecCoinFromCoin(gasPrice)), + ). + WithIsCheckTx(true) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + gasLimit := uint64(10) + fees := sdk.NewCoins(sdk.NewInt64Coin("unibi", int64(gasLimit))) + msg := &banktypes.MsgSend{ + FromAddress: deps.Sender.NibiruAddr.String(), + ToAddress: evmtest.NewEthPrivAcc().NibiruAddr.String(), + Amount: sdk.NewCoins(sdk.NewInt64Coin("unibi", 1)), + } + return buildTx(deps, true, msg, gasLimit, fees) + }, + wantErr: "invalid message", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + anteDec := evmante.NewMempoolGasPriceDecorator(deps.App.AppKeepers.EvmKeeper) + + tx := tc.txSetup(&deps) + + if tc.ctxSetup != nil { + tc.ctxSetup(&deps) + } + + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/evmante/evmante_setup_ctx.go b/app/evmante/evmante_setup_ctx.go new file mode 100644 index 000000000..893b2dc01 --- /dev/null +++ b/app/evmante/evmante_setup_ctx.go @@ -0,0 +1,45 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + errorsmod "cosmossdk.io/errors" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" +) + +// EthSetupContextDecorator is adapted from SetUpContextDecorator from cosmos-sdk, it ignores gas consumption +// by setting the gas meter to infinite +type EthSetupContextDecorator struct { + evmKeeper *EVMKeeper +} + +func NewEthSetUpContextDecorator(k *EVMKeeper) EthSetupContextDecorator { + return EthSetupContextDecorator{ + evmKeeper: k, + } +} + +func (esc EthSetupContextDecorator) AnteHandle( + ctx sdk.Context, + tx sdk.Tx, + simulate bool, + next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + // all transactions must implement GasTx + _, ok := tx.(authante.GasTx) + if !ok { + return ctx, errorsmod.Wrapf( + sdkerrors.ErrInvalidType, + "invalid transaction type %T, expected GasTx", tx, + ) + } + + // We need to setup an empty gas config so that the gas is consistent with Ethereum. + newCtx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter()). + WithKVGasConfig(storetypes.GasConfig{}). + WithTransientKVGasConfig(storetypes.GasConfig{}) + + return next(newCtx, tx, simulate) +} diff --git a/app/evmante/evmante_setup_ctx_test.go b/app/evmante/evmante_setup_ctx_test.go new file mode 100644 index 000000000..a3b3e1b9b --- /dev/null +++ b/app/evmante/evmante_setup_ctx_test.go @@ -0,0 +1,35 @@ +package evmante_test + +import ( + "math" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *TestSuite) TestEthSetupContextDecorator() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := evmante.NewEthSetUpContextDecorator(deps.App.EvmKeeper) + + s.Require().NoError(stateDB.Commit()) + tx := evmtest.HappyCreateContractTx(&deps) + + // Ante handler returns new context + newCtx, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + s.Require().NoError(err) + + // Check that ctx gas meter is set up to infinite + ctxGasMeter := newCtx.GasMeter() + s.Require().Equal(sdk.Gas(math.MaxUint64), ctxGasMeter.GasRemaining()) + + // Check that gas configs are reset to default + defaultGasConfig := storetypes.GasConfig{} + s.Require().Equal(defaultGasConfig, newCtx.KVGasConfig()) + s.Require().Equal(defaultGasConfig, newCtx.TransientKVGasConfig()) +} diff --git a/app/evmante/evmante_sigverify.go b/app/evmante/evmante_sigverify.go new file mode 100644 index 000000000..ff9918180 --- /dev/null +++ b/app/evmante/evmante_sigverify.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "math/big" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// EthSigVerificationDecorator validates an ethereum signatures +type EthSigVerificationDecorator struct { + evmKeeper *EVMKeeper +} + +// NewEthSigVerificationDecorator creates a new EthSigVerificationDecorator +func NewEthSigVerificationDecorator(k *EVMKeeper) EthSigVerificationDecorator { + return EthSigVerificationDecorator{ + evmKeeper: k, + } +} + +// AnteHandle validates checks that the registered chain id is the same as the +// one on the message, and that the signer address matches the one defined on the +// message. It's not skipped for RecheckTx, because it set `From` address which +// is critical from other ante handler to work. Failure in RecheckTx will prevent +// tx to be included into block, especially when CheckTx succeed, in which case +// user won't see the error message. +func (esvd EthSigVerificationDecorator) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + chainID := esvd.evmKeeper.EthChainID(ctx) + ethCfg := evm.EthereumConfig(chainID) + blockNum := big.NewInt(ctx.BlockHeight()) + signer := gethcore.MakeSigner(ethCfg, blockNum) + + for _, msg := range tx.GetMsgs() { + msgEthTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return ctx, errors.Wrapf( + sdkerrors.ErrUnknownRequest, + "invalid message type %T, expected %T", msg, (*evm.MsgEthereumTx)(nil), + ) + } + + ethTx := msgEthTx.AsTransaction() + if !ethTx.Protected() { + return ctx, errors.Wrapf( + sdkerrors.ErrNotSupported, + "rejected unprotected Ethereum transaction. "+ + "Please EIP155 sign your transaction to protect it against replay-attacks", + ) + } + + sender, err := signer.Sender(ethTx) + if err != nil { + return ctx, errors.Wrapf( + sdkerrors.ErrorInvalidSigner, + "couldn't retrieve sender address from the ethereum transaction: %s", + err.Error(), + ) + } + + // set up the sender to the transaction field if not already + msgEthTx.From = sender.Hex() + } + + return next(ctx, tx, simulate) +} diff --git a/app/evmante/evmante_sigverify_test.go b/app/evmante/evmante_sigverify_test.go new file mode 100644 index 000000000..d6a7998b1 --- /dev/null +++ b/app/evmante/evmante_sigverify_test.go @@ -0,0 +1,86 @@ +package evmante_test + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + tf "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" +) + +var InvalidChainID = big.NewInt(987654321) + +func (s *TestSuite) TestEthSigVerificationDecorator() { + testCases := []struct { + name string + txSetup func(deps *evmtest.TestDeps) sdk.Tx + wantErr string + }{ + { + name: "sad: unsigned tx", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + tx := evmtest.HappyCreateContractTx(deps) + return tx + }, + wantErr: "rejected unprotected Ethereum transaction", + }, + { + name: "sad: non ethereum tx", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return legacytx.StdTx{ + Msgs: []sdk.Msg{ + &tf.MsgMint{}, + }, + } + }, + wantErr: "invalid message", + }, + { + name: "sad: ethereum tx invalid chain id", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + tx := evmtest.HappyCreateContractTx(deps) + gethSigner := gethcore.LatestSignerForChainID(InvalidChainID) + err := tx.Sign(gethSigner, deps.Sender.KeyringSigner) + s.Require().NoError(err) + return tx + }, + wantErr: "invalid chain id for signer", + }, + { + name: "happy: signed ethereum tx", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + tx := evmtest.HappyCreateContractTx(deps) + gethSigner := gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) + err := tx.Sign(gethSigner, deps.Sender.KeyringSigner) + s.Require().NoError(err) + return tx + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := evmante.NewEthSigVerificationDecorator(deps.App.AppKeepers.EvmKeeper) + + tx := tc.txSetup(&deps) + s.Require().NoError(stateDB.Commit()) + + deps.Ctx = deps.Ctx.WithIsCheckTx(true) + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/evmante/evmante_validate_basic.go b/app/evmante/evmante_validate_basic.go new file mode 100644 index 000000000..a9fcc8586 --- /dev/null +++ b/app/evmante/evmante_validate_basic.go @@ -0,0 +1,154 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "errors" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// EthValidateBasicDecorator is adapted from ValidateBasicDecorator from cosmos-sdk, it ignores ErrNoSignatures +type EthValidateBasicDecorator struct { + evmKeeper *EVMKeeper +} + +// NewEthValidateBasicDecorator creates a new EthValidateBasicDecorator +func NewEthValidateBasicDecorator(k *EVMKeeper) EthValidateBasicDecorator { + return EthValidateBasicDecorator{ + evmKeeper: k, + } +} + +// AnteHandle handles basic validation of tx +func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + // no need to validate basic on recheck tx, call next antehandler + if ctx.IsReCheckTx() { + return next(ctx, tx, simulate) + } + + err := tx.ValidateBasic() + // ErrNoSignatures is fine with eth tx + if err != nil && !errors.Is(err, sdkerrors.ErrNoSignatures) { + return ctx, errorsmod.Wrap(err, "tx basic validation failed") + } + + // For eth type cosmos tx, some fields should be verified as zero values, + // since we will only verify the signature against the hash of the MsgEthereumTx.Data + wrapperTx, ok := tx.(protoTxProvider) + if !ok { + return ctx, errorsmod.Wrapf( + sdkerrors.ErrUnknownRequest, + "invalid tx type %T, didn't implement interface protoTxProvider", + tx, + ) + } + + protoTx := wrapperTx.GetProtoTx() + body := protoTx.Body + if body.Memo != "" || body.TimeoutHeight != uint64(0) || len(body.NonCriticalExtensionOptions) > 0 { + return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, + "for eth tx body Memo TimeoutHeight NonCriticalExtensionOptions should be empty") + } + + if len(body.ExtensionOptions) != 1 { + return ctx, errorsmod.Wrap( + sdkerrors.ErrInvalidRequest, + "for eth tx length of ExtensionOptions should be 1", + ) + } + + authInfo := protoTx.AuthInfo + if len(authInfo.SignerInfos) > 0 { + return ctx, errorsmod.Wrap( + sdkerrors.ErrInvalidRequest, + "for eth tx AuthInfo SignerInfos should be empty", + ) + } + + if authInfo.Fee.Payer != "" || authInfo.Fee.Granter != "" { + return ctx, errorsmod.Wrap( + sdkerrors.ErrInvalidRequest, + "for eth tx AuthInfo Fee payer and granter should be empty", + ) + } + + sigs := protoTx.Signatures + if len(sigs) > 0 { + return ctx, errorsmod.Wrap( + sdkerrors.ErrInvalidRequest, + "for eth tx Signatures should be empty", + ) + } + + txFee := sdk.Coins{} + txGasLimit := uint64(0) + + baseFeeMicronibi := vbd.evmKeeper.BaseFeeMicronibiPerGas(ctx) + + for _, msg := range protoTx.GetMsgs() { + msgEthTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return ctx, errorsmod.Wrapf( + sdkerrors.ErrUnknownRequest, + "invalid message type %T, expected %T", msg, (*evm.MsgEthereumTx)(nil), + ) + } + + // Validate `From` field + if msgEthTx.From != "" { + return ctx, errorsmod.Wrapf( + sdkerrors.ErrInvalidRequest, + "invalid From %s, expect empty string", msgEthTx.From, + ) + } + + txGasLimit += msgEthTx.GetGas() + + txData, err := evm.UnpackTxData(msgEthTx.Data) + if err != nil { + return ctx, errorsmod.Wrap(err, "failed to unpack MsgEthereumTx Data") + } + + if baseFeeMicronibi == nil && txData.TxType() == gethcore.DynamicFeeTxType { + return ctx, errorsmod.Wrap( + gethcore.ErrTxTypeNotSupported, + "dynamic fee tx not supported", + ) + } + + // Compute fees using effective fee to enforce 1unibi minimum gas price + effectiveFeeMicronibi := evm.WeiToNative(txData.EffectiveFeeWei(evm.BASE_FEE_WEI)) + txFee = txFee.Add( + sdk.Coin{ + Denom: evm.EVMBankDenom, + Amount: sdkmath.NewIntFromBigInt(effectiveFeeMicronibi), + }, + ) + } + + if !authInfo.Fee.Amount.IsEqual(txFee) { + return ctx, errorsmod.Wrapf( + sdkerrors.ErrInvalidRequest, + "invalid AuthInfo Fee Amount (%s != %s)", + authInfo.Fee.Amount, + txFee, + ) + } + + if authInfo.Fee.GasLimit != txGasLimit { + return ctx, errorsmod.Wrapf( + sdkerrors.ErrInvalidRequest, + "invalid AuthInfo Fee GasLimit (%d != %d)", + authInfo.Fee.GasLimit, + txGasLimit, + ) + } + + return next(ctx, tx, simulate) +} diff --git a/app/evmante/evmante_validate_basic_test.go b/app/evmante/evmante_validate_basic_test.go new file mode 100644 index 000000000..2aa7910dd --- /dev/null +++ b/app/evmante/evmante_validate_basic_test.go @@ -0,0 +1,268 @@ +package evmante_test + +import ( + "math/big" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *TestSuite) TestEthValidateBasicDecorator() { + testCases := []struct { + name string + ctxSetup func(deps *evmtest.TestDeps) + txSetup func(deps *evmtest.TestDeps) sdk.Tx + paramsSetup func(deps *evmtest.TestDeps) evm.Params + wantErr string + }{ + { + name: "happy: properly built eth tx", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + tx, err := evmtest.HappyCreateContractTx(deps).BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + return tx + }, + wantErr: "", + }, + { + name: "sad: fail to set params", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return evmtest.HappyCreateContractTx(deps) + }, + paramsSetup: func(deps *evmtest.TestDeps) evm.Params { + return evm.Params{ + CreateFuntokenFee: sdk.NewInt(-1), + } + }, + wantErr: "createFuntokenFee cannot be negative: -1", + }, + { + name: "happy: ctx recheck should ignore validation", + ctxSetup: func(deps *evmtest.TestDeps) { + deps.Ctx = deps.Ctx.WithIsReCheckTx(true) + }, + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return evmtest.HappyCreateContractTx(deps) + }, + wantErr: "", + }, + { + name: "sad: fail chain id basic validation", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + return evmtest.HappyCreateContractTx(deps) + }, + wantErr: "invalid chain-id", + }, + { + name: "sad: tx not implementing protoTxProvider", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + tx := evmtest.HappyCreateContractTx(deps) + gethSigner := gethcore.LatestSignerForChainID(InvalidChainID) + err := tx.Sign(gethSigner, deps.Sender.KeyringSigner) + s.Require().NoError(err) + return tx + }, + wantErr: "didn't implement interface protoTxProvider", + }, + { + name: "sad: eth tx with memo should fail", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + txBuilder.SetMemo("memo") + tx, err := evmtest.HappyCreateContractTx(deps).BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + return tx + }, + wantErr: "invalid request", + }, + { + name: "sad: eth tx with fee payer should fail", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + txBuilder.SetFeePayer(testutil.AccAddress()) + tx, err := evmtest.HappyCreateContractTx(deps).BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + return tx + }, + wantErr: "invalid request", + }, + { + name: "sad: eth tx with fee granter should fail", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + txBuilder.SetFeeGranter(testutil.AccAddress()) + tx, err := evmtest.HappyCreateContractTx(deps).BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + return tx + }, + wantErr: "invalid request", + }, + { + name: "sad: eth tx with signatures should fail", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + sigV2 := signing.SignatureV2{ + PubKey: deps.Sender.PrivKey.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: deps.EncCfg.TxConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: 0, + } + err := txBuilder.SetSignatures(sigV2) + s.Require().NoError(err) + txMsg := evmtest.HappyCreateContractTx(deps) + + gethSigner := gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) + err = txMsg.Sign(gethSigner, deps.Sender.KeyringSigner) + s.Require().NoError(err) + + tx, err := txMsg.BuildTx(txBuilder, eth.EthBaseDenom) + s.Require().NoError(err) + return tx + }, + wantErr: "tx AuthInfo SignerInfos should be empty", + }, + { + name: "sad: tx without extension options should fail", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + chainID := deps.App.EvmKeeper.EthChainID(deps.Ctx) + gasLimit := uint64(10) + fees := sdk.NewCoins(sdk.NewInt64Coin("unibi", int64(gasLimit))) + msg := buildEthMsg(chainID, gasLimit, deps.Sender.NibiruAddr.String(), nil) + return buildTx(deps, false, msg, gasLimit, fees) + }, + wantErr: "for eth tx length of ExtensionOptions should be 1", + }, + { + name: "sad: tx with non evm message", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + gasLimit := uint64(10) + fees := sdk.NewCoins(sdk.NewInt64Coin("unibi", int64(gasLimit))) + msg := &banktypes.MsgSend{ + FromAddress: deps.Sender.NibiruAddr.String(), + ToAddress: evmtest.NewEthPrivAcc().NibiruAddr.String(), + Amount: sdk.NewCoins(sdk.NewInt64Coin("unibi", 1)), + } + return buildTx(deps, true, msg, gasLimit, fees) + }, + wantErr: "invalid message", + }, + { + name: "sad: tx with from value set should fail", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + chainID := deps.App.EvmKeeper.EthChainID(deps.Ctx) + gasLimit := uint64(10) + fees := sdk.NewCoins(sdk.NewInt64Coin("unibi", int64(gasLimit))) + msg := buildEthMsg(chainID, gasLimit, deps.Sender.NibiruAddr.String(), nil) + return buildTx(deps, true, msg, gasLimit, fees) + }, + wantErr: "invalid From", + }, + { + name: "sad: tx with fee <> msg fee", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + chainID := deps.App.EvmKeeper.EthChainID(deps.Ctx) + gasLimit := uint64(10) + fees := sdk.NewCoins(sdk.NewInt64Coin("unibi", 5)) + msg := buildEthMsg(chainID, gasLimit, "", nil) + return buildTx(deps, true, msg, gasLimit, fees) + }, + wantErr: "invalid AuthInfo Fee Amount", + }, + { + name: "sad: tx with gas limit <> msg gas limit", + txSetup: func(deps *evmtest.TestDeps) sdk.Tx { + chainID := deps.App.EvmKeeper.EthChainID(deps.Ctx) + gasLimit := uint64(10) + fees := sdk.NewCoins(sdk.NewInt64Coin("unibi", int64(gasLimit))) + msg := buildEthMsg(chainID, gasLimit, "", nil) + return buildTx(deps, true, msg, 5, fees) + }, + wantErr: "invalid AuthInfo Fee GasLimit", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := evmante.NewEthValidateBasicDecorator(deps.App.AppKeepers.EvmKeeper) + + tx := tc.txSetup(&deps) + s.Require().NoError(stateDB.Commit()) + + if tc.ctxSetup != nil { + tc.ctxSetup(&deps) + } + var err error + if tc.paramsSetup != nil { + err = deps.EvmKeeper.SetParams(deps.Ctx, tc.paramsSetup(&deps)) + } + + if err == nil { + _, err = anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + } + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} + +func buildEthMsg( + chainID *big.Int, + gasLimit uint64, + from string, + to *common.Address, +) *evm.MsgEthereumTx { + ethContractCreationTxParams := &evm.EvmTxArgs{ + ChainID: chainID, + Nonce: 1, + Amount: big.NewInt(10), + GasLimit: gasLimit, + GasPrice: big.NewInt(1), + To: to, + } + tx := evm.NewTx(ethContractCreationTxParams) + tx.From = from + return tx +} + +func buildTx( + deps *evmtest.TestDeps, + ethExtentions bool, + msg sdk.Msg, + gasLimit uint64, + fees sdk.Coins, +) sdk.FeeTx { + txBuilder, _ := deps.EncCfg.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder) + if ethExtentions { + option, _ := codectypes.NewAnyWithValue(&evm.ExtensionOptionsEthereumTx{}) + txBuilder.SetExtensionOptions(option) + } + err := txBuilder.SetMsgs(msg) + if err != nil { + panic(err) + } + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetFeeAmount(fees) + + return txBuilder.GetTx() +} diff --git a/app/evmante/evmante_verify_eth_acc.go b/app/evmante/evmante_verify_eth_acc.go new file mode 100644 index 000000000..e62af5191 --- /dev/null +++ b/app/evmante/evmante_verify_eth_acc.go @@ -0,0 +1,80 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +// AnteDecVerifyEthAcc validates an account balance checks +type AnteDecVerifyEthAcc struct { + evmKeeper *EVMKeeper + accountKeeper evm.AccountKeeper +} + +// NewAnteDecVerifyEthAcc creates a new EthAccountVerificationDecorator +func NewAnteDecVerifyEthAcc(k *EVMKeeper, ak evm.AccountKeeper) AnteDecVerifyEthAcc { + return AnteDecVerifyEthAcc{ + evmKeeper: k, + accountKeeper: ak, + } +} + +// AnteHandle validates checks that the sender balance is greater than the total +// transaction cost. The account will be set to store if it doesn't exist, i.e. +// cannot be found on store. +// +// This AnteHandler decorator will fail if: +// - any of the msgs is not a MsgEthereumTx +// - from address is empty +// - account balance is lower than the transaction cost +func (anteDec AnteDecVerifyEthAcc) AnteHandle( + ctx sdk.Context, + tx sdk.Tx, + simulate bool, + next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + for i, msg := range tx.GetMsgs() { + msgEthTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return ctx, errors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evm.MsgEthereumTx)(nil)) + } + + txData, err := evm.UnpackTxData(msgEthTx.Data) + if err != nil { + return ctx, errors.Wrapf(err, "failed to unpack tx data any for tx %d", i) + } + + // sender address should be in the tx cache from the previous AnteHandle call + from := msgEthTx.GetFrom() + if from.Empty() { + return ctx, errors.Wrap(sdkerrors.ErrInvalidAddress, "from address cannot be empty") + } + + // check whether the sender address is EOA + fromAddr := gethcommon.BytesToAddress(from) + acct := anteDec.evmKeeper.GetAccount(ctx, fromAddr) + + if acct == nil { + acc := anteDec.accountKeeper.NewAccountWithAddress(ctx, from) + anteDec.accountKeeper.SetAccount(ctx, acc) + acct = statedb.NewEmptyAccount() + } else if acct.IsContract() { + return ctx, errors.Wrapf(sdkerrors.ErrInvalidType, + "the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash) + } + + if err := keeper.CheckSenderBalance( + evm.NativeToWei(acct.BalanceNative), txData, + ); err != nil { + return ctx, errors.Wrap(err, "failed to check sender balance") + } + } + return next(ctx, tx, simulate) +} diff --git a/app/evmante/evmante_verify_eth_acc_test.go b/app/evmante/evmante_verify_eth_acc_test.go new file mode 100644 index 000000000..2af951aa5 --- /dev/null +++ b/app/evmante/evmante_verify_eth_acc_test.go @@ -0,0 +1,92 @@ +package evmante_test + +import ( + "math/big" + + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/app/evmante" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +func (s *TestSuite) TestAnteDecoratorVerifyEthAcc_CheckTx() { + testCases := []struct { + name string + beforeTxSetup func(deps *evmtest.TestDeps, sdb *statedb.StateDB) + txSetup func(deps *evmtest.TestDeps) *evm.MsgEthereumTx + wantErr string + }{ + { + name: "happy: sender with funds", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + sdb.AddBalance(deps.Sender.EthAddr, evm.NativeToWei(happyGasLimit())) + }, + txSetup: evmtest.HappyCreateContractTx, + wantErr: "", + }, + { + name: "sad: sender has insufficient gas balance", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) {}, + txSetup: evmtest.HappyCreateContractTx, + wantErr: "sender balance < tx cost", + }, + { + name: "sad: sender cannot be a contract -> no contract bytecode", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) { + // Force account to be a smart contract + sdb.SetCode(deps.Sender.EthAddr, []byte("evm bytecode stuff")) + }, + txSetup: evmtest.HappyCreateContractTx, + wantErr: "sender is not EOA", + }, + { + name: "sad: invalid tx", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) {}, + txSetup: func(deps *evmtest.TestDeps) *evm.MsgEthereumTx { + return new(evm.MsgEthereumTx) + }, + wantErr: "failed to unpack tx data", + }, + { + name: "sad: empty from addr", + beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) {}, + txSetup: func(deps *evmtest.TestDeps) *evm.MsgEthereumTx { + tx := evmtest.HappyCreateContractTx(deps) + tx.From = "" + return tx + }, + wantErr: "from address cannot be empty", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + stateDB := deps.NewStateDB() + anteDec := evmante.NewAnteDecVerifyEthAcc(deps.App.AppKeepers.EvmKeeper, &deps.App.AppKeepers.AccountKeeper) + + tc.beforeTxSetup(&deps, stateDB) + tx := tc.txSetup(&deps) + s.Require().NoError(stateDB.Commit()) + + deps.Ctx = deps.Ctx.WithIsCheckTx(true) + _, err := anteDec.AnteHandle( + deps.Ctx, tx, false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} + +func happyGasLimit() *big.Int { + return new(big.Int).SetUint64( + gethparams.TxGasContractCreation + 888, + // 888 is a cushion to account for KV store reads and writes + ) +} diff --git a/app/evmante/interfaces.go b/app/evmante/interfaces.go new file mode 100644 index 000000000..46e3af6e1 --- /dev/null +++ b/app/evmante/interfaces.go @@ -0,0 +1,14 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmante + +import ( + "github.com/cosmos/cosmos-sdk/types/tx" + + evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" +) + +type EVMKeeper = evmkeeper.Keeper + +type protoTxProvider interface { + GetProtoTx() *tx.Tx +} diff --git a/app/evmante/suite_test.go b/app/evmante/suite_test.go new file mode 100644 index 000000000..621778582 --- /dev/null +++ b/app/evmante/suite_test.go @@ -0,0 +1,77 @@ +package evmante_test + +import ( + "testing" + + "cosmossdk.io/math" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/app" +) + +type TestSuite struct { + suite.Suite + + encCfg app.EncodingConfig +} + +func TestAppTestSuite(t *testing.T) { + suite.Run(t, new(TestSuite)) +} + +func (s *TestSuite) SetupSuite() { + s.encCfg = app.MakeEncodingConfig() +} + +func (s *TestSuite) DefaultGenesisCopy() app.GenesisState { + return app.NewDefaultGenesisState(s.encCfg.Codec) +} + +func (s *TestSuite) TestGenesis() { + getDefaultStakingGenesis := func() *stakingtypes.GenesisState { + genStaking := new(stakingtypes.GenesisState) + s.encCfg.Codec.MustUnmarshalJSON( + app.StakingModule{}.DefaultGenesis(s.encCfg.Codec), + genStaking, + ) + return genStaking + } + + gens := []*stakingtypes.GenesisState{} + gens = append(gens, getDefaultStakingGenesis()) + + genStaking := getDefaultStakingGenesis() + genStaking.Params.MinCommissionRate = math.LegacyZeroDec() + gens = append(gens, genStaking) + + for _, tc := range []struct { + name string + gen *stakingtypes.GenesisState + wantErr string + }{ + { + name: "default should work fine", + gen: gens[0], + }, + { + name: "zero commission should fail", + gen: gens[1], + wantErr: "min_commission must be positive", + }, + } { + s.Run(tc.name, func() { + genStakingJson := s.encCfg.Codec.MustMarshalJSON(tc.gen) + err := app.StakingModule{}.ValidateGenesis( + s.encCfg.Codec, + s.encCfg.TxConfig, + genStakingJson, + ) + if tc.wantErr != "" { + s.ErrorContains(err, tc.wantErr) + return + } + s.NoError(err) + }) + } +} diff --git a/app/export.go b/app/export.go index 37fe4efd5..34a8e36ff 100644 --- a/app/export.go +++ b/app/export.go @@ -29,13 +29,13 @@ func (app *NibiruApp) ExportAppStateAndValidators( app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) } - genState := app.mm.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) + genState := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) appState, err := json.MarshalIndent(genState, "", " ") if err != nil { return servertypes.ExportedApp{}, err } - validators, err := staking.WriteValidators(ctx, app.stakingKeeper) + validators, err := staking.WriteValidators(ctx, app.StakingKeeper) return servertypes.ExportedApp{ AppState: appState, Validators: validators, @@ -72,13 +72,13 @@ func (app *NibiruApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs /* Handle fee distribution state. */ // withdraw all validator commission - app.stakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) return false }) // withdraw all delegator rewards - dels := app.stakingKeeper.GetAllDelegations(ctx) + dels := app.StakingKeeper.GetAllDelegations(ctx) for _, delegation := range dels { valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) if err != nil { @@ -103,7 +103,7 @@ func (app *NibiruApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs ctx = ctx.WithBlockHeight(0) // reinitialize all validators - app.stakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { // donate any unwithdrawn outstanding reward fraction tokens to the community pool scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) feePool := app.DistrKeeper.GetFeePool(ctx) @@ -143,20 +143,20 @@ func (app *NibiruApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs /* Handle staking state. */ // iterate through redelegations, reset creation height - app.stakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { + app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { for i := range red.Entries { red.Entries[i].CreationHeight = 0 } - app.stakingKeeper.SetRedelegation(ctx, red) + app.StakingKeeper.SetRedelegation(ctx, red) return false }) // iterate through unbonding delegations, reset creation height - app.stakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { + app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { for i := range ubd.Entries { ubd.Entries[i].CreationHeight = 0 } - app.stakingKeeper.SetUnbondingDelegation(ctx, ubd) + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) return false }) @@ -168,7 +168,7 @@ func (app *NibiruApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs for ; iter.Valid(); iter.Next() { addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) - validator, found := app.stakingKeeper.GetValidator(ctx, addr) + validator, found := app.StakingKeeper.GetValidator(ctx, addr) if !found { panic("expected validator, not found") } @@ -178,13 +178,13 @@ func (app *NibiruApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs validator.Jailed = true } - app.stakingKeeper.SetValidator(ctx, validator) + app.StakingKeeper.SetValidator(ctx, validator) counter++ } iter.Close() - _, err := app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) if err != nil { log.Fatal(err) } diff --git a/app/genesis.go b/app/genesis.go index ead2fc081..0710a5b9a 100644 --- a/app/genesis.go +++ b/app/genesis.go @@ -78,6 +78,7 @@ func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { gen := ModuleBasics.DefaultGenesis(cdc) authGenesis := new(authtypes.GenesisState) + authtypes.DefaultGenesisState() cdc.MustUnmarshalJSON(gen[authtypes.ModuleName], authGenesis) return gen diff --git a/app/ibc_test.go b/app/ibc_test.go index e3541b18b..369eb88da 100644 --- a/app/ibc_test.go +++ b/app/ibc_test.go @@ -4,18 +4,20 @@ import ( "encoding/json" "testing" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ibctesting "github.com/cosmos/ibc-go/v7/testing" "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" ) // init changes the value of 'DefaultTestingAppInit' to use custom initialization. func init() { ibctesting.DefaultTestingAppInit = SetupNibiruTestingApp + testapp.EnsureNibiruPrefix() } /* @@ -32,7 +34,7 @@ func SetupNibiruTestingApp() ( // Create genesis state encCdc := app.MakeEncodingConfig() - genesisState := app.NewDefaultGenesisState(encCdc.Marshaler) + genesisState := app.NewDefaultGenesisState(encCdc.Codec) testapp.SetDefaultSudoGenesis(genesisState) return nibiruApp, genesisState @@ -90,7 +92,7 @@ func (suite *IBCTestSuite) TestHandleMsgTransfer() { path := NewIBCTestingTransferPath(suite.chainA, suite.chainB) suite.coordinator.Setup(path) - amount, ok := sdk.NewIntFromString("9223372036854775808") // 2^63 (one above int64) + amount, ok := math.NewIntFromString("9223372036854775808") // 2^63 (one above int64) suite.Require().True(ok) coinToSendToB := sdk.NewCoin(sdk.DefaultBondDenom, amount) @@ -195,7 +197,7 @@ func (suite *IBCTestSuite) TestHandleMsgTransfer() { // check that module account escrow address is empty escrowAddress := transfertypes.GetEscrowAddress(packet.GetDestPort(), packet.GetDestChannel()) balance = chainBApp.BankKeeper.GetBalance(suite.chainB.GetContext(), escrowAddress, sdk.DefaultBondDenom) - suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, sdk.ZeroInt()), balance) + suite.Require().Equal(sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt()), balance) // check that balance on chain B is empty balance = chainCApp.BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), voucherDenomTrace.IBCDenom()) diff --git a/app/keepers.go b/app/keepers.go index 0a2d028c1..fc24f561e 100644 --- a/app/keepers.go +++ b/app/keepers.go @@ -29,8 +29,6 @@ import ( authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/auth/vesting" - vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" "github.com/cosmos/cosmos-sdk/x/authz" authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" @@ -105,46 +103,48 @@ import ( // --------------------------------------------------------------- // Nibiru Custom Modules - "github.com/NibiruChain/nibiru/x/common" - "github.com/NibiruChain/nibiru/x/devgas/v1" - devgaskeeper "github.com/NibiruChain/nibiru/x/devgas/v1/keeper" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" - "github.com/NibiruChain/nibiru/x/epochs" - epochskeeper "github.com/NibiruChain/nibiru/x/epochs/keeper" - epochstypes "github.com/NibiruChain/nibiru/x/epochs/types" - "github.com/NibiruChain/nibiru/x/genmsg" - "github.com/NibiruChain/nibiru/x/inflation" - inflationkeeper "github.com/NibiruChain/nibiru/x/inflation/keeper" - inflationtypes "github.com/NibiruChain/nibiru/x/inflation/types" - oracle "github.com/NibiruChain/nibiru/x/oracle" - oraclekeeper "github.com/NibiruChain/nibiru/x/oracle/keeper" - oracletypes "github.com/NibiruChain/nibiru/x/oracle/types" - "github.com/NibiruChain/nibiru/x/sudo" - "github.com/NibiruChain/nibiru/x/sudo/keeper" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" - - tokenfactory "github.com/NibiruChain/nibiru/x/tokenfactory" - tokenfactorykeeper "github.com/NibiruChain/nibiru/x/tokenfactory/keeper" - tokenfactorytypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/app/keepers" + "github.com/NibiruChain/nibiru/v2/app/wasmext" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1" + devgaskeeper "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/epochs" + epochskeeper "github.com/NibiruChain/nibiru/v2/x/epochs/keeper" + epochstypes "github.com/NibiruChain/nibiru/v2/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmmodule" + evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + "github.com/NibiruChain/nibiru/v2/x/genmsg" + "github.com/NibiruChain/nibiru/v2/x/inflation" + inflationkeeper "github.com/NibiruChain/nibiru/v2/x/inflation/keeper" + inflationtypes "github.com/NibiruChain/nibiru/v2/x/inflation/types" + oracle "github.com/NibiruChain/nibiru/v2/x/oracle" + oraclekeeper "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + oracletypes "github.com/NibiruChain/nibiru/v2/x/oracle/types" + + "github.com/NibiruChain/nibiru/v2/x/sudo" + "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" + + tokenfactory "github.com/NibiruChain/nibiru/v2/x/tokenfactory" + tokenfactorykeeper "github.com/NibiruChain/nibiru/v2/x/tokenfactory/keeper" + tokenfactorytypes "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) type AppKeepers struct { - // AccountKeeper encodes/decodes accounts using the go-amino (binary) encoding/decoding library - AccountKeeper authkeeper.AccountKeeper - // BankKeeper defines a module interface that facilitates the transfer of coins between accounts - BankKeeper bankkeeper.Keeper + keepers.PublicKeepers + privateKeepers +} + +type privateKeepers struct { capabilityKeeper *capabilitykeeper.Keeper - stakingKeeper *stakingkeeper.Keeper slashingKeeper slashingkeeper.Keeper - /* DistrKeeper is the keeper of the distribution store */ - DistrKeeper distrkeeper.Keeper - GovKeeper govkeeper.Keeper - crisisKeeper crisiskeeper.Keeper - upgradeKeeper upgradekeeper.Keeper - paramsKeeper paramskeeper.Keeper - authzKeeper authzkeeper.Keeper - FeeGrantKeeper feegrantkeeper.Keeper - ConsensusParamsKeeper consensusparamkeeper.Keeper + crisisKeeper crisiskeeper.Keeper + upgradeKeeper upgradekeeper.Keeper + paramsKeeper paramskeeper.Keeper + authzKeeper authzkeeper.Keeper // -------------------------------------------------------------------- // IBC keepers @@ -162,30 +162,6 @@ type AppKeepers struct { ibcTransferKeeper ibctransferkeeper.Keeper icaControllerKeeper icacontrollerkeeper.Keeper icaHostKeeper icahostkeeper.Keeper - - // make scoped keepers public for test purposes - ScopedIBCKeeper capabilitykeeper.ScopedKeeper - ScopedICAControllerKeeper capabilitykeeper.ScopedKeeper - ScopedICAHostKeeper capabilitykeeper.ScopedKeeper - ScopedTransferKeeper capabilitykeeper.ScopedKeeper - - // make IBC modules public for test purposes - // these modules are never directly routed to by the IBC Router - FeeMockModule ibcmock.IBCModule - - // --------------- - // Nibiru keepers - // --------------- - EpochsKeeper epochskeeper.Keeper - OracleKeeper oraclekeeper.Keeper - InflationKeeper inflationkeeper.Keeper - SudoKeeper keeper.Keeper - DevGasKeeper devgaskeeper.Keeper - TokenFactoryKeeper tokenfactorykeeper.Keeper - - // WASM keepers - WasmKeeper wasmkeeper.Keeper - ScopedWasmKeeper capabilitykeeper.ScopedKeeper } func initStoreKeys() ( @@ -224,8 +200,10 @@ func initStoreKeys() ( wasmtypes.StoreKey, devgastypes.StoreKey, tokenfactorytypes.StoreKey, + + evm.StoreKey, ) - tkeys = sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + tkeys = sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evm.TransientKey) memKeys = sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) return keys, tkeys, memKeys } @@ -241,13 +219,14 @@ func (app *NibiruApp) InitKeepers( tkeys := app.tkeys memKeys := app.memKeys + govModuleAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() app.paramsKeeper = initParamsKeeper( appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey], ) // set the BaseApp's parameter store - app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper(appCodec, keys[consensusparamtypes.StoreKey], authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper(appCodec, keys[consensusparamtypes.StoreKey], govModuleAddr) bApp.SetParamStore(&app.ConsensusParamsKeeper) /* Add capabilityKeeper and ScopeToModule for the ibc module @@ -272,37 +251,43 @@ func (app *NibiruApp) InitKeepers( // seal capability keeper after scoping modules // app.capabilityKeeper.Seal() - // add keepers + // TODO: chore(upgrade): Potential breaking change on AccountKeeper dur + // to ProtoBaseAccount replacement. app.AccountKeeper = authkeeper.NewAccountKeeper( appCodec, keys[authtypes.StoreKey], - authtypes.ProtoBaseAccount, + eth.ProtoBaseAccount, maccPerms, sdk.GetConfig().GetBech32AccountAddrPrefix(), - authtypes.NewModuleAddress(govtypes.ModuleName).String(), - ) - app.BankKeeper = bankkeeper.NewBaseKeeper( - appCodec, - keys[banktypes.StoreKey], - app.AccountKeeper, - BlockedAddresses(), - authtypes.NewModuleAddress(govtypes.ModuleName).String(), + govModuleAddr, ) - app.stakingKeeper = stakingkeeper.NewKeeper( + + nibiruBankKeeper := &evmkeeper.NibiruBankKeeper{ + BaseKeeper: bankkeeper.NewBaseKeeper( + appCodec, + keys[banktypes.StoreKey], + app.AccountKeeper, + BlockedAddresses(), + govModuleAddr, + ), + StateDB: nil, + } + app.BankKeeper = nibiruBankKeeper + app.StakingKeeper = stakingkeeper.NewKeeper( appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), + govModuleAddr, ) app.DistrKeeper = distrkeeper.NewKeeper( appCodec, keys[distrtypes.StoreKey], app.AccountKeeper, app.BankKeeper, - app.stakingKeeper, + app.StakingKeeper, authtypes.FeeCollectorName, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), + govModuleAddr, ) invCheckPeriod := cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)) @@ -312,7 +297,7 @@ func (app *NibiruApp) InitKeepers( invCheckPeriod, app.BankKeeper, authtypes.FeeCollectorName, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), + govModuleAddr, ) app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper) @@ -331,7 +316,7 @@ func (app *NibiruApp) InitKeepers( appCodec, homePath, app.BaseApp, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), + govModuleAddr, ) // register the staking hooks @@ -340,11 +325,11 @@ func (app *NibiruApp) InitKeepers( appCodec, legacyAmino, keys[slashingtypes.StoreKey], - app.stakingKeeper, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), + app.StakingKeeper, + govModuleAddr, ) - app.stakingKeeper.SetHooks( + app.StakingKeeper.SetHooks( stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.slashingKeeper.Hooks()), ) @@ -356,8 +341,14 @@ func (app *NibiruApp) InitKeepers( ) // ---------------------------------- Nibiru Chain x/ keepers + + app.SudoKeeper = keeper.NewKeeper( + appCodec, keys[sudotypes.StoreKey], + ) + app.OracleKeeper = oraclekeeper.NewKeeper(appCodec, keys[oracletypes.StoreKey], - app.AccountKeeper, app.BankKeeper, app.DistrKeeper, app.stakingKeeper, app.slashingKeeper, + app.AccountKeeper, app.BankKeeper, app.DistrKeeper, app.StakingKeeper, app.slashingKeeper, + app.SudoKeeper, distrtypes.ModuleName, ) @@ -365,13 +356,9 @@ func (app *NibiruApp) InitKeepers( appCodec, keys[epochstypes.StoreKey], ) - app.SudoKeeper = keeper.NewKeeper( - appCodec, keys[sudotypes.StoreKey], - ) - app.InflationKeeper = inflationkeeper.NewKeeper( appCodec, keys[inflationtypes.StoreKey], app.GetSubspace(inflationtypes.ModuleName), - app.AccountKeeper, app.BankKeeper, app.DistrKeeper, app.stakingKeeper, app.SudoKeeper, authtypes.FeeCollectorName, + app.AccountKeeper, app.BankKeeper, app.DistrKeeper, app.StakingKeeper, app.SudoKeeper, authtypes.FeeCollectorName, ) app.EpochsKeeper.SetHooks( @@ -381,13 +368,25 @@ func (app *NibiruApp) InitKeepers( ), ) + evmKeeper := evmkeeper.NewKeeper( + appCodec, + keys[evm.StoreKey], + tkeys[evm.TransientKey], + authtypes.NewModuleAddress(govtypes.ModuleName), + app.AccountKeeper, + nibiruBankKeeper, + app.StakingKeeper, + cast.ToString(appOpts.Get("evm.tracer")), + ) + app.EvmKeeper = &evmKeeper + // ---------------------------------- IBC keepers app.ibcKeeper = ibckeeper.NewKeeper( appCodec, keys[ibcexported.StoreKey], app.GetSubspace(ibcexported.ModuleName), - app.stakingKeeper, + app.StakingKeeper, app.upgradeKeeper, app.ScopedIBCKeeper, ) @@ -450,32 +449,42 @@ func (app *NibiruApp) InitKeepers( // NOTE: This keeper depends on all of pointers to the the Keepers to which // it binds. Thus, it must be instantiated after those keepers have been // assigned. - // For example, if there are bindings for the x/perp module, then the app - // passed to GetWasmOpts must already have a non-nil PerpKeeper. + // For example, if there are bindings for the x/inflation module, then the app + // passed to GetWasmOpts must already have a non-nil InflationKeeper. supportedFeatures := strings.Join(wasmdapp.AllCapabilities(), ",") + + wmha := wasmext.MsgHandlerArgs{ + Router: app.MsgServiceRouter(), + Ics4Wrapper: app.ibcFeeKeeper, + ChannelKeeper: app.ibcKeeper.ChannelKeeper, + CapabilityKeeper: app.ScopedWasmKeeper, + BankKeeper: app.BankKeeper, + Unpacker: appCodec, + PortSource: app.ibcTransferKeeper, + } + app.WasmMsgHandlerArgs = wmha app.WasmKeeper = wasmkeeper.NewKeeper( appCodec, keys[wasmtypes.StoreKey], app.AccountKeeper, app.BankKeeper, - app.stakingKeeper, + app.StakingKeeper, distrkeeper.NewQuerier(app.DistrKeeper), - app.ibcFeeKeeper, // ISC4 Wrapper: fee IBC middleware - app.ibcKeeper.ChannelKeeper, + wmha.Ics4Wrapper, // ISC4 Wrapper: fee IBC middleware + wmha.ChannelKeeper, &app.ibcKeeper.PortKeeper, - app.ScopedWasmKeeper, - app.ibcTransferKeeper, - app.MsgServiceRouter(), + wmha.CapabilityKeeper, + wmha.PortSource, + wmha.Router, app.GRPCQueryRouter(), wasmDir, wasmConfig, supportedFeatures, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), - GetWasmOpts(*app, appOpts)..., + govModuleAddr, + GetWasmOpts(*app, appOpts, wmha)..., ) // DevGas uses WasmKeeper - govModuleAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() app.DevGasKeeper = devgaskeeper.NewKeeper( keys[devgastypes.StoreKey], appCodec, @@ -486,7 +495,6 @@ func (app *NibiruApp) InitKeepers( govModuleAddr, ) - // TokenFactory has wasm bindings app.TokenFactoryKeeper = tokenfactorykeeper.NewKeeper( keys[tokenfactorytypes.StoreKey], appCodec, @@ -501,7 +509,7 @@ func (app *NibiruApp) InitKeepers( // Create evidence keeper. // This keeper automatically includes an evidence router. app.evidenceKeeper = *evidencekeeper.NewKeeper( - appCodec, keys[evidencetypes.StoreKey], app.stakingKeeper, + appCodec, keys[evidencetypes.StoreKey], app.StakingKeeper, app.slashingKeeper, ) @@ -585,10 +593,10 @@ func (app *NibiruApp) InitKeepers( keys[govtypes.StoreKey], app.AccountKeeper, app.BankKeeper, - app.stakingKeeper, + app.StakingKeeper, app.MsgServiceRouter(), govConfig, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), + govModuleAddr, ) govKeeper.SetLegacyRouter(govRouter) @@ -608,18 +616,17 @@ func (app *NibiruApp) initAppModules( return []module.AppModule{ // core modules genutil.NewAppModule( - app.AccountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx, + app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, encodingConfig.TxConfig, ), auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), - vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), capability.NewAppModule(appCodec, *app.capabilityKeeper, false), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), - slashing.NewAppModule(appCodec, app.slashingKeeper, app.AccountKeeper, app.BankKeeper, app.stakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), - distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.stakingKeeper, app.GetSubspace(distrtypes.ModuleName)), - staking.NewAppModule(appCodec, app.stakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + slashing.NewAppModule(appCodec, app.slashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), upgrade.NewAppModule(&app.upgradeKeeper), params.NewAppModule(app.paramsKeeper), authzmodule.NewAppModule(appCodec, app.authzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), @@ -627,7 +634,7 @@ func (app *NibiruApp) initAppModules( // Nibiru modules oracle.NewAppModule(appCodec, app.OracleKeeper, app.AccountKeeper, app.BankKeeper, app.SudoKeeper), epochs.NewAppModule(appCodec, app.EpochsKeeper), - inflation.NewAppModule(app.InflationKeeper, app.AccountKeeper, *app.stakingKeeper), + inflation.NewAppModule(app.InflationKeeper, app.AccountKeeper, *app.StakingKeeper), sudo.NewAppModule(appCodec, app.SudoKeeper), genmsg.NewAppModule(app.MsgServiceRouter()), @@ -638,9 +645,11 @@ func (app *NibiruApp) initAppModules( ibcfee.NewAppModule(app.ibcFeeKeeper), ica.NewAppModule(&app.icaControllerKeeper, &app.icaHostKeeper), + evmmodule.NewAppModule(app.EvmKeeper, app.AccountKeeper), + // wasm wasm.NewAppModule( - appCodec, &app.WasmKeeper, app.stakingKeeper, app.AccountKeeper, + appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.MsgServiceRouter(), app.GetSubspace(wasmtypes.ModuleName)), devgas.NewAppModule( @@ -691,7 +700,6 @@ func orderedModuleNames() []string { authz.ModuleName, feegrant.ModuleName, paramstypes.ModuleName, - vestingtypes.ModuleName, // -------------------------------------------------------------------- // Native x/ Modules @@ -707,13 +715,16 @@ func orderedModuleNames() []string { ibcfeetypes.ModuleName, icatypes.ModuleName, + // -------------------------------------------------------------------- + evm.ModuleName, + // -------------------------------------------------------------------- // CosmWasm wasmtypes.ModuleName, devgastypes.ModuleName, tokenfactorytypes.ModuleName, - // Should be before genmsg + // Everything else should be before genmsg genmsg.ModuleName, } } @@ -738,23 +749,23 @@ func (app *NibiruApp) initModuleManager( encodingConfig EncodingConfig, skipGenesisInvariants bool, ) { - app.mm = module.NewManager( + app.ModuleManager = module.NewManager( app.initAppModules(encodingConfig, skipGenesisInvariants)..., ) orderedModules := orderedModuleNames() - app.mm.SetOrderBeginBlockers(orderedModules...) - app.mm.SetOrderEndBlockers(orderedModules...) - app.mm.SetOrderInitGenesis(orderedModules...) - app.mm.SetOrderExportGenesis(orderedModules...) + app.ModuleManager.SetOrderBeginBlockers(orderedModules...) + app.ModuleManager.SetOrderEndBlockers(orderedModules...) + app.ModuleManager.SetOrderInitGenesis(orderedModules...) + app.ModuleManager.SetOrderExportGenesis(orderedModules...) // Uncomment if you want to set a custom migration order here. // app.mm.SetOrderMigrations(custom order) - app.mm.RegisterInvariants(&app.crisisKeeper) + app.ModuleManager.RegisterInvariants(&app.crisisKeeper) app.configurator = module.NewConfigurator( app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) - app.mm.RegisterServices(app.configurator) + app.ModuleManager.RegisterServices(app.configurator) // see https://github.com/cosmos/cosmos-sdk/blob/666c345ad23ddda9523cc5cd1b71187d91c26f34/simapp/upgrades.go#L35-L57 for _, subspace := range app.paramsKeeper.GetSubspaces() { @@ -803,13 +814,13 @@ func ModuleBasicManager() module.BasicManager { evidence.AppModuleBasic{}, authzmodule.AppModuleBasic{}, groupmodule.AppModuleBasic{}, - vesting.AppModuleBasic{}, // ibc 'AppModuleBasic's ibc.AppModuleBasic{}, ibctransfer.AppModuleBasic{}, ibctm.AppModuleBasic{}, ica.AppModuleBasic{}, // native x/ + evmmodule.AppModuleBasic{}, oracle.AppModuleBasic{}, epochs.AppModuleBasic{}, inflation.AppModuleBasic{}, @@ -826,7 +837,7 @@ func ModuleAccPerms() map[string][]string { return map[string][]string{ authtypes.FeeCollectorName: nil, distrtypes.ModuleName: nil, - inflationtypes.ModuleName: {authtypes.Minter}, + inflationtypes.ModuleName: {authtypes.Minter, authtypes.Burner}, stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, govtypes.ModuleName: {authtypes.Burner}, @@ -835,6 +846,7 @@ func ModuleAccPerms() map[string][]string { ibcfeetypes.ModuleName: {}, icatypes.ModuleName: {}, + evm.ModuleName: {authtypes.Minter, authtypes.Burner}, epochstypes.ModuleName: {}, sudotypes.ModuleName: {}, common.TreasuryPoolModuleAccount: {}, @@ -843,7 +855,6 @@ func ModuleAccPerms() map[string][]string { } } -// initParamsKeeper init params perpammkeeper and its subspaces func initParamsKeeper( appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey, @@ -873,34 +884,13 @@ func initParamsKeeper( return paramsKeeper } -// TODO: Simulation manager -func (app *NibiruApp) InitSimulationManager( +func (app *NibiruApp) initSimulationManager( appCodec codec.Codec, ) { - //// create the simulation manager and define the order of the modules for deterministic simulations - //// - //// NOTE: this is not required apps that don't use the simulator for fuzz testing - //// transactions - //epochsModule := epochs.NewAppModule(appCodec, app.EpochsKeeper) - //app.sm = module.NewSimulationManager( - // auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts), - // bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), - // feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), - // gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), - // staking.NewAppModule(appCodec, app.stakingKeeper, app.AccountKeeper, app.BankKeeper), - // distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.stakingKeeper), - // slashing.NewAppModule(appCodec, app.slashingKeeper, app.AccountKeeper, app.BankKeeper, app.stakingKeeper), - // params.NewAppModule(app.paramsKeeper), - // authzmodule.NewAppModule(appCodec, app.authzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), - // // native x/ - // epochsModule, - // // ibc - // capability.NewAppModule(appCodec, *app.capabilityKeeper), - // evidence.NewAppModule(app.evidenceKeeper), - // ibc.NewAppModule(app.ibcKeeper), - // ibctransfer.NewAppModule(app.transferKeeper), - // ibcfee.NewAppModule(app.ibcFeeKeeper), - //) - // - //app.sm.RegisterStoreDecoders() + overrideModules := map[string]module.AppModuleSimulation{ + authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + } + app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules) + + app.sm.RegisterStoreDecoders() } diff --git a/app/keepers/all_keepers.go b/app/keepers/all_keepers.go new file mode 100644 index 000000000..43278e3d5 --- /dev/null +++ b/app/keepers/all_keepers.go @@ -0,0 +1,74 @@ +package keepers + +import ( + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + _ "github.com/cosmos/cosmos-sdk/client/docs/statik" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + + // --------------------------------------------------------------- + // IBC imports + + ibcmock "github.com/cosmos/ibc-go/v7/testing/mock" + + // --------------------------------------------------------------- + // Nibiru Custom Modules + + "github.com/NibiruChain/nibiru/v2/app/wasmext" + devgaskeeper "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper" + epochskeeper "github.com/NibiruChain/nibiru/v2/x/epochs/keeper" + evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + inflationkeeper "github.com/NibiruChain/nibiru/v2/x/inflation/keeper" + oraclekeeper "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + + "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" + + tokenfactorykeeper "github.com/NibiruChain/nibiru/v2/x/tokenfactory/keeper" +) + +type PublicKeepers struct { + // AccountKeeper encodes/decodes accounts using the go-amino (binary) encoding/decoding library + AccountKeeper authkeeper.AccountKeeper + // BankKeeper defines a module interface that facilitates the transfer of coins between accounts + BankKeeper bankkeeper.Keeper + StakingKeeper *stakingkeeper.Keeper + /* DistrKeeper is the keeper of the distribution store */ + DistrKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + ConsensusParamsKeeper consensusparamkeeper.Keeper + + // make scoped keepers public for test purposes + ScopedIBCKeeper capabilitykeeper.ScopedKeeper + ScopedICAControllerKeeper capabilitykeeper.ScopedKeeper + ScopedICAHostKeeper capabilitykeeper.ScopedKeeper + ScopedTransferKeeper capabilitykeeper.ScopedKeeper + + // make IBC modules public for test purposes + // these modules are never directly routed to by the IBC Router + FeeMockModule ibcmock.IBCModule + + // --------------- + // Nibiru keepers + // --------------- + EpochsKeeper epochskeeper.Keeper + OracleKeeper oraclekeeper.Keeper + InflationKeeper inflationkeeper.Keeper + SudoKeeper keeper.Keeper + DevGasKeeper devgaskeeper.Keeper + TokenFactoryKeeper tokenfactorykeeper.Keeper + EvmKeeper *evmkeeper.Keeper + + // WASM keepers + WasmKeeper wasmkeeper.Keeper + WasmMsgHandlerArgs wasmext.MsgHandlerArgs + + ScopedWasmKeeper capabilitykeeper.ScopedKeeper +} diff --git a/app/modules.go b/app/modules.go index c64155dad..c597c94a9 100644 --- a/app/modules.go +++ b/app/modules.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + "cosmossdk.io/math" sdkclient "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -17,6 +18,8 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/cosmos/cosmos-sdk/x/staking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/NibiruChain/nibiru/v2/app/appconst" ) // BankModule defines a custom wrapper around the x/bank module's AppModuleBasic @@ -29,13 +32,13 @@ type BankModule struct { func (BankModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { denomMetadata := banktypes.Metadata{ Description: "The native staking token of the Nibiru network.", - Base: BondDenom, + Base: appconst.BondDenom, Name: DisplayDenom, Display: DisplayDenom, Symbol: DisplayDenom, DenomUnits: []*banktypes.DenomUnit{ { - Denom: BondDenom, + Denom: appconst.BondDenom, Exponent: 0, Aliases: []string{ "micronibi", @@ -65,8 +68,8 @@ var _ module.HasGenesisBasics = (*StakingModule)(nil) // DefaultGenesis returns custom Nibiru x/staking module genesis state. func (StakingModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { genState := stakingtypes.DefaultGenesisState() - genState.Params.BondDenom = BondDenom - genState.Params.MinCommissionRate = sdk.MustNewDecFromStr("0.05") + genState.Params.BondDenom = appconst.BondDenom + genState.Params.MinCommissionRate = math.LegacyMustNewDecFromStr("0.05") return cdc.MustMarshalJSON(genState) } @@ -99,7 +102,7 @@ type CrisisModule struct { // DefaultGenesis returns custom Nibiru x/crisis module genesis state. func (CrisisModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { genState := crisistypes.DefaultGenesisState() - genState.ConstantFee = sdk.NewCoin(BondDenom, genState.ConstantFee.Amount) + genState.ConstantFee = sdk.NewCoin(appconst.BondDenom, genState.ConstantFee.Amount) return cdc.MustMarshalJSON(genState) } @@ -113,7 +116,7 @@ type GovModule struct { func (GovModule) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { genState := govtypes.DefaultGenesisState() genState.Params.MinDeposit = sdk.NewCoins( - sdk.NewCoin(BondDenom, govtypes.DefaultMinDepositTokens)) + sdk.NewCoin(appconst.BondDenom, govtypes.DefaultMinDepositTokens)) return cdc.MustMarshalJSON(genState) } diff --git a/app/modules_test.go b/app/modules_test.go index ec977aad6..44e59599f 100644 --- a/app/modules_test.go +++ b/app/modules_test.go @@ -3,11 +3,11 @@ package app_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/v2/app" ) type TestSuite struct { @@ -25,14 +25,14 @@ func (s *TestSuite) SetupSuite() { } func (s *TestSuite) DefaultGenesisCopy() app.GenesisState { - return app.NewDefaultGenesisState(s.encCfg.Marshaler) + return app.NewDefaultGenesisState(s.encCfg.Codec) } func (s *TestSuite) TestGenesis() { getDefaultStakingGenesis := func() *stakingtypes.GenesisState { genStaking := new(stakingtypes.GenesisState) - s.encCfg.Marshaler.MustUnmarshalJSON( - app.StakingModule{}.DefaultGenesis(s.encCfg.Marshaler), + s.encCfg.Codec.MustUnmarshalJSON( + app.StakingModule{}.DefaultGenesis(s.encCfg.Codec), genStaking, ) return genStaking @@ -42,7 +42,7 @@ func (s *TestSuite) TestGenesis() { gens = append(gens, getDefaultStakingGenesis()) genStaking := getDefaultStakingGenesis() - genStaking.Params.MinCommissionRate = sdk.ZeroDec() + genStaking.Params.MinCommissionRate = math.LegacyZeroDec() gens = append(gens, genStaking) for _, tc := range []struct { @@ -60,10 +60,10 @@ func (s *TestSuite) TestGenesis() { wantErr: "min_commission must be positive", }, } { - s.T().Run(tc.name, func(t *testing.T) { - genStakingJson := s.encCfg.Marshaler.MustMarshalJSON(tc.gen) + s.Run(tc.name, func() { + genStakingJson := s.encCfg.Codec.MustMarshalJSON(tc.gen) err := app.StakingModule{}.ValidateGenesis( - s.encCfg.Marshaler, + s.encCfg.Codec, s.encCfg.TxConfig, genStakingJson, ) diff --git a/app/server/config/server_config.go b/app/server/config/server_config.go new file mode 100644 index 000000000..f393983a9 --- /dev/null +++ b/app/server/config/server_config.go @@ -0,0 +1,495 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package config + +import ( + "errors" + "fmt" + "path" + "time" + + tracerslogger "github.com/ethereum/go-ethereum/eth/tracers/logger" + + "github.com/spf13/viper" + + "github.com/cometbft/cometbft/libs/strings" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/server/config" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" +) + +const ( + // DefaultAPIEnable is the default value for the parameter that defines if the cosmos REST API server is enabled + DefaultAPIEnable = false + + // DefaultGRPCEnable is the default value for the parameter that defines if the gRPC server is enabled + DefaultGRPCEnable = false + + // DefaultGRPCWebEnable is the default value for the parameter that defines if the gRPC web server is enabled + DefaultGRPCWebEnable = false + + // DefaultJSONRPCEnable is the default value for the parameter that defines if the JSON-RPC server is enabled + DefaultJSONRPCEnable = false + + // DefaultRosettaEnable is the default value for the parameter that defines if the Rosetta API server is enabled + DefaultRosettaEnable = false + + // DefaultTelemetryEnable is the default value for the parameter that defines if the telemetry is enabled + DefaultTelemetryEnable = false + + // DefaultGRPCAddress is the default address the gRPC server binds to. + DefaultGRPCAddress = "0.0.0.0:9900" + + // DefaultJSONRPCAddress is the default address the JSON-RPC server binds to. + DefaultJSONRPCAddress = "127.0.0.1:8545" + + // DefaultJSONRPCWsAddress is the default address the JSON-RPC WebSocket server binds to. + DefaultJSONRPCWsAddress = "127.0.0.1:8546" + + // DefaultJsonRPCMetricsAddress is the default address the JSON-RPC Metrics server binds to. + DefaultJSONRPCMetricsAddress = "127.0.0.1:6065" + + // DefaultEVMTracer is the default vm.Tracer type + DefaultEVMTracer = "" + + // DefaultFixRevertGasRefundHeight is the default height at which to overwrite gas refund + DefaultFixRevertGasRefundHeight = 0 + + // DefaultMaxTxGasWanted is the default gas wanted for each eth tx returned in ante handler in check tx mode + DefaultMaxTxGasWanted = 0 + + // DefaultEthCallGasLimit is the default cap on gas that can be used in eth_call/estimateGas + DefaultEthCallGasLimit uint64 = 25_000_000 + + // DefaultFilterCap is the default cap for total number of filters that can be created + DefaultFilterCap int32 = 200 + + // DefaultFeeHistoryCap is the default cap for total number of blocks that can be fetched + DefaultFeeHistoryCap int32 = 100 + + // DefaultLogsCap is the default cap of results returned from single 'eth_getLogs' query + DefaultLogsCap int32 = 10000 + + // DefaultBlockRangeCap is the default cap of block range allowed for 'eth_getLogs' query + DefaultBlockRangeCap int32 = 10000 + + // DefaultEVMTimeout is the default timeout for eth_call + DefaultEVMTimeout = 5 * time.Second + + // DefaultTxFeeCap is the default tx-fee cap for sending a transaction + DefaultTxFeeCap float64 = 1.0 + + // DefaultHTTPTimeout is the default read/write timeout of the http json-rpc server + DefaultHTTPTimeout = 30 * time.Second + + // DefaultHTTPIdleTimeout is the default idle timeout of the http json-rpc server + DefaultHTTPIdleTimeout = 120 * time.Second + + // DefaultAllowUnprotectedTxs value is false + DefaultAllowUnprotectedTxs = false + + // DefaultMaxOpenConnections represents the amount of open connections (unlimited = 0) + DefaultMaxOpenConnections = 0 + + // DefaultGasAdjustment value to use as default in gas-adjustment flag + DefaultGasAdjustment = 1.2 + + // DefaultZeroCopy is the default value that defines if + // the zero-copied slices must be retained beyond current block's execution + // the sdk address cache will be disabled if zero-copy is enabled + DefaultZeroCopy = false +) + +var evmTracers = []string{"json", "markdown", "struct", "access_list"} + +// Config defines the server's top level configuration. It includes the default app config +// from the SDK as well as the EVM configuration to enable the JSON-RPC APIs. +type Config struct { + config.Config `mapstructure:",squash"` + + EVM EVMConfig `mapstructure:"evm"` + JSONRPC JSONRPCConfig `mapstructure:"json-rpc"` + TLS TLSConfig `mapstructure:"tls"` +} + +// EVMConfig defines the application configuration values for the EVM. +type EVMConfig struct { + // Tracer defines vm.Tracer type that the EVM will use if the node is run in + // trace mode. Default: 'json'. + Tracer string `mapstructure:"tracer"` + TracerOpts tracerslogger.Config `mapstucture:"tracer_opts"` + // MaxTxGasWanted defines the gas wanted for each eth tx returned in ante handler in check tx mode. + MaxTxGasWanted uint64 `mapstructure:"max-tx-gas-wanted"` +} + +// JSONRPCConfig defines configuration for the EVM RPC server. +type JSONRPCConfig struct { + // API defines a list of JSON-RPC namespaces that should be enabled + API []string `mapstructure:"api"` + // Address defines the HTTP server to listen on + Address string `mapstructure:"address"` + // WsAddress defines the WebSocket server to listen on + WsAddress string `mapstructure:"ws-address"` + // GasCap is the global gas cap for eth-call variants. + GasCap uint64 `mapstructure:"gas-cap"` + // EVMTimeout is the global timeout for eth-call. + EVMTimeout time.Duration `mapstructure:"evm-timeout"` + // TxFeeCap is the global tx-fee cap for send transaction + TxFeeCap float64 `mapstructure:"txfee-cap"` + // FilterCap is the global cap for total number of filters that can be created. + FilterCap int32 `mapstructure:"filter-cap"` + // FeeHistoryCap is the global cap for total number of blocks that can be fetched + FeeHistoryCap int32 `mapstructure:"feehistory-cap"` + // Enable defines if the EVM RPC server should be enabled. + Enable bool `mapstructure:"enable"` + // LogsCap defines the max number of results can be returned from single `eth_getLogs` query. + LogsCap int32 `mapstructure:"logs-cap"` + // BlockRangeCap defines the max block range allowed for `eth_getLogs` query. + BlockRangeCap int32 `mapstructure:"block-range-cap"` + // HTTPTimeout is the read/write timeout of http json-rpc server. + HTTPTimeout time.Duration `mapstructure:"http-timeout"` + // HTTPIdleTimeout is the idle timeout of http json-rpc server. + HTTPIdleTimeout time.Duration `mapstructure:"http-idle-timeout"` + // AllowUnprotectedTxs restricts unprotected (non EIP155 signed) transactions to be submitted via + // the node's RPC when global parameter is disabled. + AllowUnprotectedTxs bool `mapstructure:"allow-unprotected-txs"` + // MaxOpenConnections sets the maximum number of simultaneous connections + // for the server listener. + MaxOpenConnections int `mapstructure:"max-open-connections"` + // EnableIndexer defines if enable the custom indexer service. + EnableIndexer bool `mapstructure:"enable-indexer"` + // MetricsAddress defines the metrics server to listen on + MetricsAddress string `mapstructure:"metrics-address"` + // FixRevertGasRefundHeight defines the upgrade height for fix of revert gas refund logic when transaction reverted + FixRevertGasRefundHeight int64 `mapstructure:"fix-revert-gas-refund-height"` +} + +// TLSConfig defines the certificate and matching private key for the server. +type TLSConfig struct { + // CertificatePath the file path for the certificate .pem file + CertificatePath string `mapstructure:"certificate-path"` + // KeyPath the file path for the key .pem file + KeyPath string `mapstructure:"key-path"` +} + +// AppConfig helps to override default appConfig template and configs. +// return "", nil if no custom configuration is required for the application. +func AppConfig(denom string) (string, any) { + // Optionally allow the chain developer to overwrite the SDK's default + // server config. + customAppConfig := DefaultConfig() + + // The SDK's default minimum gas price is set to "" (empty value) inside + // app.toml. If left empty by validators, the node will halt on startup. + // However, the chain developer can set a default app.toml value for their + // validators here. + // + // In summary: + // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their + // own app.toml config, + // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their + // own app.toml to override, or use this default value. + if denom != "" { + customAppConfig.Config.MinGasPrices = "0" + denom + } + + customAppTemplate := config.DefaultConfigTemplate + DefaultConfigTemplate + + return customAppTemplate, *customAppConfig +} + +// DefaultConfig returns server's default configuration. +func DefaultConfig() *Config { + defaultSDKConfig := config.DefaultConfig() + defaultSDKConfig.API.Enable = DefaultAPIEnable + defaultSDKConfig.GRPC.Enable = DefaultGRPCEnable + defaultSDKConfig.GRPCWeb.Enable = DefaultGRPCWebEnable + defaultSDKConfig.Rosetta.Enable = DefaultRosettaEnable + defaultSDKConfig.Telemetry.Enabled = DefaultTelemetryEnable + + return &Config{ + Config: *defaultSDKConfig, + EVM: *DefaultEVMConfig(), + JSONRPC: *DefaultJSONRPCConfig(), + TLS: *DefaultTLSConfig(), + } +} + +// DefaultEVMConfig returns the default EVM configuration +func DefaultEVMConfig() *EVMConfig { + return &EVMConfig{ + Tracer: DefaultEVMTracer, + TracerOpts: tracerslogger.Config{ + EnableMemory: false, // disable + DisableStack: false, // enable stack + DisableStorage: false, // enable storage + EnableReturnData: false, // disable + Debug: true, // enable debug + Limit: 0, + Overrides: nil, + }, + MaxTxGasWanted: DefaultMaxTxGasWanted, + } +} + +// Validate returns an error if the tracer type is invalid. +func (c EVMConfig) Validate() error { + if c.Tracer != "" && !strings.StringInSlice(c.Tracer, evmTracers) { + return fmt.Errorf("invalid tracer type %s, available types: %v", c.Tracer, evmTracers) + } + + return nil +} + +// GetDefaultAPINamespaces returns the default list of JSON-RPC namespaces that should be enabled +func GetDefaultAPINamespaces() []string { + return []string{"eth", "net", "web3"} +} + +// GetAPINamespaces returns the all the available JSON-RPC API namespaces. +func GetAPINamespaces() []string { + return []string{"web3", "eth", "net", "txpool", "debug"} +} + +// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default +func DefaultJSONRPCConfig() *JSONRPCConfig { + return &JSONRPCConfig{ + Enable: false, + API: GetDefaultAPINamespaces(), + Address: DefaultJSONRPCAddress, + WsAddress: DefaultJSONRPCWsAddress, + GasCap: DefaultEthCallGasLimit, + EVMTimeout: DefaultEVMTimeout, + TxFeeCap: DefaultTxFeeCap, + FilterCap: DefaultFilterCap, + FeeHistoryCap: DefaultFeeHistoryCap, + BlockRangeCap: DefaultBlockRangeCap, + LogsCap: DefaultLogsCap, + HTTPTimeout: DefaultHTTPTimeout, + HTTPIdleTimeout: DefaultHTTPIdleTimeout, + AllowUnprotectedTxs: DefaultAllowUnprotectedTxs, + MaxOpenConnections: DefaultMaxOpenConnections, + EnableIndexer: false, + MetricsAddress: DefaultJSONRPCMetricsAddress, + FixRevertGasRefundHeight: DefaultFixRevertGasRefundHeight, + } +} + +// Validate returns an error if the JSON-RPC configuration fields are invalid. +func (c JSONRPCConfig) Validate() error { + if c.Enable && len(c.API) == 0 { + return errors.New("cannot enable JSON-RPC without defining any API namespace") + } + + if c.FilterCap < 0 { + return errors.New("JSON-RPC filter-cap cannot be negative") + } + + if c.FeeHistoryCap <= 0 { + return errors.New("JSON-RPC feehistory-cap cannot be negative or 0") + } + + if c.TxFeeCap < 0 { + return errors.New("JSON-RPC tx fee cap cannot be negative") + } + + if c.EVMTimeout < 0 { + return errors.New("JSON-RPC EVM timeout duration cannot be negative") + } + + if c.LogsCap < 0 { + return errors.New("JSON-RPC logs cap cannot be negative") + } + + if c.BlockRangeCap < 0 { + return errors.New("JSON-RPC block range cap cannot be negative") + } + + if c.HTTPTimeout < 0 { + return errors.New("JSON-RPC HTTP timeout duration cannot be negative") + } + + if c.HTTPIdleTimeout < 0 { + return errors.New("JSON-RPC HTTP idle timeout duration cannot be negative") + } + + // check for duplicates + seenAPIs := make(map[string]bool) + for _, api := range c.API { + if seenAPIs[api] { + return fmt.Errorf("repeated API namespace '%s'", api) + } + + seenAPIs[api] = true + } + + return nil +} + +// DefaultTLSConfig returns the default TLS configuration +func DefaultTLSConfig() *TLSConfig { + return &TLSConfig{ + CertificatePath: "", + KeyPath: "", + } +} + +// Validate returns an error if the TLS certificate and key file extensions are invalid. +func (c TLSConfig) Validate() error { + certExt := path.Ext(c.CertificatePath) + + if c.CertificatePath != "" && certExt != ".pem" { + return fmt.Errorf("invalid extension %s for certificate path %s, expected '.pem'", certExt, c.CertificatePath) + } + + keyExt := path.Ext(c.KeyPath) + + if c.KeyPath != "" && keyExt != ".pem" { + return fmt.Errorf("invalid extension %s for key path %s, expected '.pem'", keyExt, c.KeyPath) + } + + return nil +} + +// GetConfig returns a fully parsed Config object. +func GetConfig(v *viper.Viper) (Config, error) { + conf := DefaultConfig() + if err := v.Unmarshal(conf); err != nil { + return Config{}, fmt.Errorf("error extracting app config: %w", err) + } + return *conf, nil +} + +// ValidateBasic returns an error any of the application configuration fields are invalid +func (c Config) ValidateBasic() error { + if err := c.EVM.Validate(); err != nil { + return errorsmod.Wrapf(errortypes.ErrAppConfig, "invalid evm config value: %s", err.Error()) + } + + if err := c.JSONRPC.Validate(); err != nil { + return errorsmod.Wrapf(errortypes.ErrAppConfig, "invalid json-rpc config value: %s", err.Error()) + } + + if err := c.TLS.Validate(); err != nil { + return errorsmod.Wrapf(errortypes.ErrAppConfig, "invalid tls config value: %s", err.Error()) + } + + return c.Config.ValidateBasic() +} + +// DefaultConfigTemplate defines the configuration template for the EVM RPC configuration +const DefaultConfigTemplate = ` +############################################################################### +### EVM Configuration ### +############################################################################### + +[evm] + +# Tracer defines the 'vm.Tracer' type that the EVM will use when the node is run in +# debug mode. To enable tracing use the '--evm.tracer' flag when starting your node. +# Valid types are: json|struct|access_list|markdown +tracer = "{{ .EVM.Tracer }}" + +# MaxTxGasWanted defines the gas wanted for each eth tx returned in ante handler in check tx mode. +max-tx-gas-wanted = {{ .EVM.MaxTxGasWanted }} + +[evm.tracer_opts] + +# Enable the capture of EVM memory state at each +# execution step. This can be useful for debugging complex contracts but may +# significantly increase the volume of logged data. +memory = false + +# Enable the capture of contract storage changes. By default, storage +# modifications are logged. Disabling storage capture can significantly reduce +# log size for contracts with many storage operations. +stack = true + +# Enable the capture of contract storage changes. +storage = true + +# enable return-data capture +return-data = false + +# enable debug capture +debug = true + +# Maximum length of the tracer output. Zero means unlimited. +limit = 0 + +############################################################################### +### JSON RPC Configuration ### +############################################################################### + +[json-rpc] + +# Enable defines if the gRPC server should be enabled. +enable = {{ .JSONRPC.Enable }} + +# Address defines the EVM RPC HTTP server address to bind to. +address = "{{ .JSONRPC.Address }}" + +# Address defines the EVM WebSocket server address to bind to. +ws-address = "{{ .JSONRPC.WsAddress }}" + +# API defines a list of JSON-RPC namespaces that should be enabled +# Example: "eth,txpool,net,debug,web3" +api = "{{range $index, $elmt := .JSONRPC.API}}{{if $index}},{{$elmt}}{{else}}{{$elmt}}{{end}}{{end}}" + +# GasCap sets a cap on gas that can be used in eth_call/estimateGas (0=infinite). Default: 25,000,000. +gas-cap = {{ .JSONRPC.GasCap }} + +# EVMTimeout is the global timeout for eth_call. Default: 5s. +evm-timeout = "{{ .JSONRPC.EVMTimeout }}" + +# TxFeeCap is the global tx-fee cap for send transaction. Default: 1eth. +txfee-cap = {{ .JSONRPC.TxFeeCap }} + +# FilterCap sets the global cap for total number of filters that can be created +filter-cap = {{ .JSONRPC.FilterCap }} + +# FeeHistoryCap sets the global cap for total number of blocks that can be fetched +feehistory-cap = {{ .JSONRPC.FeeHistoryCap }} + +# LogsCap defines the max number of results can be returned from single 'eth_getLogs' query. +logs-cap = {{ .JSONRPC.LogsCap }} + +# BlockRangeCap defines the max block range allowed for 'eth_getLogs' query. +block-range-cap = {{ .JSONRPC.BlockRangeCap }} + +# HTTPTimeout is the read/write timeout of http json-rpc server. +http-timeout = "{{ .JSONRPC.HTTPTimeout }}" + +# HTTPIdleTimeout is the idle timeout of http json-rpc server. +http-idle-timeout = "{{ .JSONRPC.HTTPIdleTimeout }}" + +# AllowUnprotectedTxs restricts unprotected (non EIP155 signed) transactions to be submitted via +# the node's RPC when the global parameter is disabled. +allow-unprotected-txs = {{ .JSONRPC.AllowUnprotectedTxs }} + +# MaxOpenConnections sets the maximum number of simultaneous connections +# for the server listener. +max-open-connections = {{ .JSONRPC.MaxOpenConnections }} + +# EnableIndexer enables the custom transaction indexer for the EVM (ethereum transactions). +enable-indexer = {{ .JSONRPC.EnableIndexer }} + +# MetricsAddress defines the EVM Metrics server address to bind to. Pass --metrics in CLI to enable +# Prometheus metrics path: /debug/metrics/prometheus +metrics-address = "{{ .JSONRPC.MetricsAddress }}" + +# Upgrade height for fix of revert gas refund logic when transaction reverted. +fix-revert-gas-refund-height = {{ .JSONRPC.FixRevertGasRefundHeight }} + +############################################################################### +### TLS Configuration ### +############################################################################### + +[tls] + +# Certificate path defines the cert.pem file path for the TLS configuration. +certificate-path = "{{ .TLS.CertificatePath }}" + +# Key path defines the key.pem file path for the TLS configuration. +key-path = "{{ .TLS.KeyPath }}" +` diff --git a/app/server/evm_tx_indexer_cli.go b/app/server/evm_tx_indexer_cli.go new file mode 100644 index 000000000..74b9b8fd4 --- /dev/null +++ b/app/server/evm_tx_indexer_cli.go @@ -0,0 +1,133 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package server + +import ( + "fmt" + "strconv" + + "github.com/spf13/cobra" + + "github.com/NibiruChain/nibiru/v2/eth/indexer" + + tmnode "github.com/cometbft/cometbft/node" + sm "github.com/cometbft/cometbft/state" + tmstore "github.com/cometbft/cometbft/store" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" +) + +func NewEVMTxIndexCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "evm-tx-index [minBlockNumber|last-indexed] [maxBlockNumber|latest]", + Short: "Index historical evm blocks and transactions", + Long: `Command is useful for catching up if the node experienced a period +with EVMTxIndexer turned off or was stopped without proper closing/flushing EVMIndexerDB. +Processes blocks from minBlockNumber to maxBlockNumber, indexes evm txs. + +- minBlockNumber: min block to start indexing. Supply "last-indexed" to start with the latest block available in EVMIndexerDB. +- maxBlockNumber: max block, could be a number or "latest". + +Default run before the full node/archive node start should be: + +nibid evm-tx-index last-indexed latest + `, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + serverCtx := server.GetServerContextFromCmd(cmd) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + cfg := serverCtx.Config + logger := serverCtx.Logger + evmIndexerDB, err := OpenIndexerDB(cfg.RootDir, server.GetAppDBBackend(serverCtx.Viper)) + if err != nil { + logger.Error("failed to open evm indexer DB", "error", err.Error()) + return err + } + + evmTxIndexer := indexer.NewEVMTxIndexer(evmIndexerDB, logger.With("module", "evmindex"), clientCtx) + + tmdb, err := tmnode.DefaultDBProvider(&tmnode.DBContext{ID: "blockstore", Config: cfg}) + if err != nil { + return err + } + blockStore := tmstore.NewBlockStore(tmdb) + minAvailableHeight := blockStore.Base() + maxAvailableHeight := blockStore.Height() + fmt.Printf("Block range available on the node: %d - %d\n", minAvailableHeight, maxAvailableHeight) + + var fromBlock int64 + var toBlock int64 + + // FROM block could be one of two: + // - int64 number - replaced with minAvailableHeight if too low + // - last-indexed - latest available block in EVMIndexerDB, 0 if nothing is indexed + if args[0] == "last-indexed" { + fromBlock, err = evmTxIndexer.LastIndexedBlock() + if err != nil || fromBlock < 0 { + fromBlock = 0 + } + } else { + fromBlock, err = strconv.ParseInt(args[1], 10, 64) + if err != nil { + return fmt.Errorf("cannot parse min block number: %s", args[1]) + } + if fromBlock > maxAvailableHeight { + return fmt.Errorf("maximum available block is: %d", maxAvailableHeight) + } + } + if fromBlock < minAvailableHeight { + fromBlock = minAvailableHeight + } + + // TO block could be one of two: + // - int64 number - replaced with maxAvailableHeight if too high + // - latest - latest available block in the node + if args[1] == "latest" { + toBlock = maxAvailableHeight + } else { + toBlock, err = strconv.ParseInt(args[1], 10, 64) + if err != nil { + return fmt.Errorf("cannot parse max block number: %s", args[1]) + } + if toBlock > maxAvailableHeight { + toBlock = maxAvailableHeight + } + } + if fromBlock > toBlock { + return fmt.Errorf("minBlockNumber must be less or equal to maxBlockNumber") + } + stateDB, err := tmnode.DefaultDBProvider(&tmnode.DBContext{ID: "state", Config: cfg}) + if err != nil { + return err + } + stateStore := sm.NewStore(stateDB, sm.StoreOptions{ + DiscardABCIResponses: cfg.Storage.DiscardABCIResponses, + }) + + fmt.Printf("Indexing blocks from %d to %d\n", fromBlock, toBlock) + for height := fromBlock; height <= toBlock; height++ { + block := blockStore.LoadBlock(height) + if block == nil { + return fmt.Errorf("block not found %d", height) + } + blockResults, err := stateStore.LoadABCIResponses(height) + if err != nil { + return err + } + if err := evmTxIndexer.IndexBlock(block, blockResults.DeliverTxs); err != nil { + return err + } + fmt.Println(height) + } + err = evmTxIndexer.CloseDBAndExit() + if err != nil { + return err + } + fmt.Println("Indexing complete") + return nil + }, + } + return cmd +} diff --git a/app/server/evm_tx_indexer_service.go b/app/server/evm_tx_indexer_service.go new file mode 100644 index 000000000..32e1709c5 --- /dev/null +++ b/app/server/evm_tx_indexer_service.go @@ -0,0 +1,135 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package server + +import ( + "context" + "sync/atomic" + "time" + + "github.com/cometbft/cometbft/libs/service" + rpcclient "github.com/cometbft/cometbft/rpc/client" + "github.com/cometbft/cometbft/types" + + "github.com/NibiruChain/nibiru/v2/eth/indexer" +) + +const ( + EVMTxIndexerServiceName = "EVMTxIndexerService" + + NewBlockWaitTimeout = 60 * time.Second +) + +// EVMTxIndexerService indexes transactions for json-rpc service. +type EVMTxIndexerService struct { + service.BaseService + + evmTxIndexer *indexer.EVMTxIndexer + rpcClient rpcclient.Client + cancelFunc context.CancelFunc +} + +// NewEVMIndexerService returns a new service instance. +func NewEVMIndexerService(evmTxIndexer *indexer.EVMTxIndexer, rpcClient rpcclient.Client) *EVMTxIndexerService { + indexerService := &EVMTxIndexerService{evmTxIndexer: evmTxIndexer, rpcClient: rpcClient} + indexerService.BaseService = *service.NewBaseService(nil, EVMTxIndexerServiceName, indexerService) + return indexerService +} + +// OnStart implements service.Service by subscribing for new blocks +// and indexing them by events. +func (service *EVMTxIndexerService) OnStart() error { + ctx, cancel := context.WithCancel(context.Background()) + service.cancelFunc = cancel + + status, err := service.rpcClient.Status(ctx) + if err != nil { + return err + } + + // chainHeightStorage is used within goroutine and the indexer loop so, using atomic for read/write + var chainHeightStorage int64 + atomic.StoreInt64(&chainHeightStorage, status.SyncInfo.LatestBlockHeight) + + newBlockSignal := make(chan struct{}, 1) + blockHeadersChan, err := service.rpcClient.Subscribe( + ctx, + EVMTxIndexerServiceName, + types.QueryForEvent(types.EventNewBlockHeader).String(), + 0, + ) + if err != nil { + return err + } + + // Goroutine listening for new blocks + go func(ctx context.Context) { + for { + select { + case <-ctx.Done(): + service.Logger.Info("Stopping indexer goroutine") + err := service.evmTxIndexer.CloseDBAndExit() + if err != nil { + service.Logger.Error("Error closing indexer DB", "err", err) + } + return + case msg := <-blockHeadersChan: + eventDataHeader := msg.Data.(types.EventDataNewBlockHeader) + currentChainHeight := eventDataHeader.Header.Height + chainHeight := atomic.LoadInt64(&chainHeightStorage) + if currentChainHeight > chainHeight { + atomic.StoreInt64(&chainHeightStorage, currentChainHeight) + // notify + select { + case newBlockSignal <- struct{}{}: + default: + } + } + } + } + }(ctx) + + lastIndexedHeight, err := service.evmTxIndexer.LastIndexedBlock() + if err != nil { + return err + } + if lastIndexedHeight == -1 { + lastIndexedHeight = atomic.LoadInt64(&chainHeightStorage) + } + + // Indexer loop + for { + chainHeight := atomic.LoadInt64(&chainHeightStorage) + if chainHeight <= lastIndexedHeight { + // nothing to index. wait for signal of new block + select { + case <-newBlockSignal: + case <-time.After(NewBlockWaitTimeout): + } + continue + } + for i := lastIndexedHeight + 1; i <= chainHeight; i++ { + block, err := service.rpcClient.Block(ctx, &i) + if err != nil { + service.Logger.Error("failed to fetch block", "height", i, "err", err) + break + } + blockResult, err := service.rpcClient.BlockResults(ctx, &i) + if err != nil { + service.Logger.Error("failed to fetch block result", "height", i, "err", err) + break + } + if err := service.evmTxIndexer.IndexBlock(block.Block, blockResult.TxsResults); err != nil { + service.Logger.Error("failed to index block", "height", i, "err", err) + } + lastIndexedHeight = blockResult.Height + } + } +} + +func (service *EVMTxIndexerService) OnStop() { + service.Logger.Info("Stopping EVMTxIndexerService") + if service.cancelFunc != nil { + service.Logger.Info("Calling EVMIndexerService CancelFunc") + service.cancelFunc() + } +} diff --git a/app/server/flags.go b/app/server/flags.go new file mode 100644 index 000000000..7cfb993ca --- /dev/null +++ b/app/server/flags.go @@ -0,0 +1,93 @@ +package server + +import ( + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// Tendermint/cosmos-sdk full-node start flags +const ( + WithTendermint = "with-tendermint" + Address = "address" + Transport = "transport" + TraceStore = "trace-store" + CPUProfile = "cpu-profile" + // The type of database for application and snapshots databases + AppDBBackend = "app-db-backend" +) + +// GRPC-related flags. +const ( + GRPCOnly = "grpc-only" + GRPCEnable = "grpc.enable" + GRPCAddress = "grpc.address" + GRPCWebEnable = "grpc-web.enable" + GRPCWebAddress = "grpc-web.address" +) + +// Cosmos API flags +const ( + RPCEnable = "api.enable" + EnabledUnsafeCors = "api.enabled-unsafe-cors" +) + +// JSON-RPC flags +const ( + JSONRPCEnable = "json-rpc.enable" + JSONRPCAPI = "json-rpc.api" + JSONRPCAddress = "json-rpc.address" + JSONWsAddress = "json-rpc.ws-address" + JSONRPCGasCap = "json-rpc.gas-cap" + JSONRPCEVMTimeout = "json-rpc.evm-timeout" + JSONRPCTxFeeCap = "json-rpc.txfee-cap" + JSONRPCFilterCap = "json-rpc.filter-cap" + JSONRPCLogsCap = "json-rpc.logs-cap" + JSONRPCBlockRangeCap = "json-rpc.block-range-cap" + JSONRPCHTTPTimeout = "json-rpc.http-timeout" + JSONRPCHTTPIdleTimeout = "json-rpc.http-idle-timeout" + JSONRPCAllowUnprotectedTxs = "json-rpc.allow-unprotected-txs" + JSONRPCMaxOpenConnections = "json-rpc.max-open-connections" + JSONRPCEnableIndexer = "json-rpc.enable-indexer" + JSONRPCEnableMetrics = "metrics" +) + +// EVM flags +const ( + EVMTracer = "evm.tracer" + EVMMaxTxGasWanted = "evm.max-tx-gas-wanted" +) + +// TLS flags +const ( + TLSCertPath = "tls.certificate-path" + TLSKeyPath = "tls.key-path" +) + +// AddTxFlags adds common flags for commands to post tx +func AddTxFlags(cmd *cobra.Command) (*cobra.Command, error) { + cmd.PersistentFlags().String(flags.FlagChainID, "", "Specify Chain ID for sending Tx") + cmd.PersistentFlags().String(flags.FlagFrom, "", "Name or address of private key with which to sign") + cmd.PersistentFlags().String(flags.FlagFees, "", "Fees to pay along with transaction; eg: 5000unibi") + cmd.PersistentFlags().String(flags.FlagGasPrices, "", "Gas prices to determine the transaction fee (e.g. 5000unibi)") + cmd.PersistentFlags().String(flags.FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") //nolint:lll + cmd.PersistentFlags().Float64(flags.FlagGasAdjustment, flags.DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ") //nolint:lll + cmd.PersistentFlags().StringP(flags.FlagBroadcastMode, "b", flags.BroadcastSync, "Transaction broadcasting mode (sync|async)") + cmd.PersistentFlags().String(flags.FlagKeyringBackend, keyring.BackendOS, "Select keyring's backend") + + // --gas can accept integers and "simulate" + // cmd.PersistentFlags().Var(&flags.GasFlagVar, "gas", fmt.Sprintf( + // "gas limit to set per-transaction; set to %q to calculate required gas automatically (default %d)", + // flags.GasFlagAuto, flags.DefaultGasLimit, + // )) + + // viper.BindPFlag(flags.FlagTrustNode, cmd.Flags().Lookup(flags.FlagTrustNode)) + if err := viper.BindPFlag(flags.FlagNode, cmd.PersistentFlags().Lookup(flags.FlagNode)); err != nil { + return nil, err + } + if err := viper.BindPFlag(flags.FlagKeyringBackend, cmd.PersistentFlags().Lookup(flags.FlagKeyringBackend)); err != nil { + return nil, err + } + return cmd, nil +} diff --git a/app/server/json_rpc.go b/app/server/json_rpc.go new file mode 100644 index 000000000..44b946719 --- /dev/null +++ b/app/server/json_rpc.go @@ -0,0 +1,116 @@ +package server + +import ( + "errors" + "net/http" + "time" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc/rpcapi" + + "github.com/gorilla/mux" + "github.com/rs/cors" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/types" + ethlog "github.com/ethereum/go-ethereum/log" + ethrpc "github.com/ethereum/go-ethereum/rpc" + + srvconfig "github.com/NibiruChain/nibiru/v2/app/server/config" +) + +// StartJSONRPC starts the JSON-RPC server +func StartJSONRPC( + ctx *server.Context, + clientCtx client.Context, + tmRPCAddr, + tmEndpoint string, + config *srvconfig.Config, + indexer eth.EVMTxIndexer, +) (*http.Server, chan struct{}, error) { + tmWsClientForRPCApi := ConnectTmWS(tmRPCAddr, tmEndpoint, ctx.Logger) + + logger := ctx.Logger.With("module", "geth") + ethlog.Root().SetHandler(ethlog.FuncHandler(func(r *ethlog.Record) error { + switch r.Lvl { + case ethlog.LvlTrace, ethlog.LvlDebug: + logger.Debug(r.Msg, r.Ctx...) + case ethlog.LvlInfo, ethlog.LvlWarn: + logger.Info(r.Msg, r.Ctx...) + case ethlog.LvlError, ethlog.LvlCrit: + logger.Error(r.Msg, r.Ctx...) + } + return nil + })) + + rpcServer := ethrpc.NewServer() + + allowUnprotectedTxs := config.JSONRPC.AllowUnprotectedTxs + rpcAPIArr := config.JSONRPC.API + + apis := rpcapi.GetRPCAPIs(ctx, clientCtx, tmWsClientForRPCApi, allowUnprotectedTxs, indexer, rpcAPIArr) + + for _, api := range apis { + if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil { + ctx.Logger.Error( + "failed to register service in JSON RPC namespace", + "namespace", api.Namespace, + "service", api.Service, + ) + return nil, nil, err + } + } + + r := mux.NewRouter() + r.HandleFunc("/", rpcServer.ServeHTTP).Methods("POST") + + handlerWithCors := cors.Default() + if config.API.EnableUnsafeCORS { + handlerWithCors = cors.AllowAll() + } + + httpSrv := &http.Server{ + Addr: config.JSONRPC.Address, + Handler: handlerWithCors.Handler(r), + ReadHeaderTimeout: config.JSONRPC.HTTPTimeout, + ReadTimeout: config.JSONRPC.HTTPTimeout, + WriteTimeout: config.JSONRPC.HTTPTimeout, + IdleTimeout: config.JSONRPC.HTTPIdleTimeout, + } + httpSrvDone := make(chan struct{}, 1) + + ln, err := Listen(httpSrv.Addr, config) + if err != nil { + return nil, nil, err + } + + errCh := make(chan error) + go func() { + ctx.Logger.Info("Starting JSON-RPC server", "address", config.JSONRPC.Address) + if err := httpSrv.Serve(ln); err != nil { + if errors.Is(err, http.ErrServerClosed) { + close(httpSrvDone) + return + } + + ctx.Logger.Error("failed to start JSON-RPC server", "error", err.Error()) + errCh <- err + } + }() + + select { + case err := <-errCh: + ctx.Logger.Error("failed to boot JSON-RPC server", "error", err.Error()) + return nil, nil, err + case <-time.After(types.ServerStartTime): // assume JSON RPC server started successfully + } + + ctx.Logger.Info("Starting JSON WebSocket server", "address", config.JSONRPC.WsAddress) + + // allocate separate WS connection to Tendermint + tmWsClientForRPCWs := ConnectTmWS(tmRPCAddr, tmEndpoint, ctx.Logger) + wsSrv := rpcapi.NewWebsocketsServer(clientCtx, ctx.Logger, tmWsClientForRPCWs, config) + wsSrv.Start() + return httpSrv, httpSrvDone, nil +} diff --git a/app/server/start.go b/app/server/start.go new file mode 100644 index 000000000..44a1d2d82 --- /dev/null +++ b/app/server/start.go @@ -0,0 +1,688 @@ +package server + +import ( + "context" + "fmt" + "io" + "net" + "net/http" + "os" + "os/signal" + "path/filepath" + "runtime/pprof" + "syscall" + "time" + + "github.com/NibiruChain/nibiru/v2/app/server/config" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/indexer" + + rpcclient "github.com/cometbft/cometbft/rpc/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/telemetry" + + "github.com/spf13/cobra" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + dbm "github.com/cometbft/cometbft-db" + abciserver "github.com/cometbft/cometbft/abci/server" + tcmd "github.com/cometbft/cometbft/cmd/cometbft/commands" + tmos "github.com/cometbft/cometbft/libs/os" + "github.com/cometbft/cometbft/node" + "github.com/cometbft/cometbft/p2p" + pvm "github.com/cometbft/cometbft/privval" + "github.com/cometbft/cometbft/proxy" + "github.com/cometbft/cometbft/rpc/client/local" + + "cosmossdk.io/tools/rosetta" + crgserver "cosmossdk.io/tools/rosetta/lib/server" + + ethmetricsexp "github.com/ethereum/go-ethereum/metrics/exp" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdkserver "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" + servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" + "github.com/cosmos/cosmos-sdk/server/types" + pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// StartOptions defines options that can be customized in `StartCmd` +type StartOptions struct { + AppCreator types.AppCreator + DefaultNodeHome string +} + +// NewDefaultStartOptions use the default db opener provided in tm-db. +func NewDefaultStartOptions(appCreator types.AppCreator, defaultNodeHome string) StartOptions { + return StartOptions{ + AppCreator: appCreator, + DefaultNodeHome: defaultNodeHome, + } +} + +func openDB(rootDir string, backendType dbm.BackendType) (dbm.DB, error) { + dataDir := filepath.Join(rootDir, "data") + return dbm.NewDB("application", backendType, dataDir) +} + +// StartCmd runs the service passed in, either stand-alone or in-process with +// Tendermint. +func StartCmd(opts StartOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "start", + Short: "Run the full node", + Long: `Run the full node application with Tendermint in or out of process. By +default, the application will run with Tendermint in process. + +Pruning options can be provided via the '--pruning' flag or alternatively with '--pruning-keep-recent', +'pruning-keep-every', and 'pruning-interval' together. + +For '--pruning' the options are as follows: + +default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals +nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) +everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals +custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval' + +Node halting configurations exist in the form of two flags: '--halt-height' and '--halt-time'. During +the ABCI Commit phase, the node will check if the current block height is greater than or equal to +the halt-height or if the current block time is greater than or equal to the halt-time. If so, the +node will attempt to gracefully shutdown and the block will not be committed. In addition, the node +will not be able to commit subsequent blocks. + +For profiling and benchmarking purposes, CPU profiling can be enabled via the '--cpu-profile' flag +which accepts a path for the resulting pprof file. +`, + PreRunE: func(cmd *cobra.Command, _ []string) error { + serverCtx := sdkserver.GetServerContextFromCmd(cmd) + + // Bind flags to the Context's Viper so the app construction can set + // options accordingly. + err := serverCtx.Viper.BindPFlags(cmd.Flags()) + if err != nil { + return err + } + + _, err = sdkserver.GetPruningOptionsFromFlags(serverCtx.Viper) + return err + }, + RunE: func(cmd *cobra.Command, _ []string) error { + serverCtx := sdkserver.GetServerContextFromCmd(cmd) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + withTM, _ := cmd.Flags().GetBool(WithTendermint) + if !withTM { + serverCtx.Logger.Info("starting ABCI without Tendermint") + return wrapCPUProfile(serverCtx, func() error { + return startStandAlone(serverCtx, opts) + }) + } + + serverCtx.Logger.Info("Unlocking keyring") + + // fire unlock precess for keyring + keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + if keyringBackend == keyring.BackendFile { + _, err = clientCtx.Keyring.List() + if err != nil { + return err + } + } + + serverCtx.Logger.Info("starting ABCI with Tendermint") + + // amino is needed here for backwards compatibility of REST routes + err = startInProcess(serverCtx, clientCtx, opts) + errCode, ok := err.(sdkserver.ErrorCode) + if !ok { + return err + } + + serverCtx.Logger.Debug(fmt.Sprintf("received quit signal: %d", errCode.Code)) + return nil + }, + } + + cmd.Flags().String(flags.FlagHome, opts.DefaultNodeHome, "The application home directory") + cmd.Flags().Bool(WithTendermint, true, "Run abci app embedded in-process with tendermint") + cmd.Flags().String(Address, "tcp://0.0.0.0:26658", "Listen address") + cmd.Flags().String(Transport, "socket", "Transport protocol: socket, grpc") + cmd.Flags().String(TraceStore, "", "Enable KVStore tracing to an output file") + cmd.Flags().String(sdkserver.FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 5000unibi)") //nolint:lll + cmd.Flags().IntSlice(sdkserver.FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary") + cmd.Flags().Uint64(sdkserver.FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node") + cmd.Flags().Uint64(sdkserver.FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node") + cmd.Flags().Bool(sdkserver.FlagInterBlockCache, true, "Enable inter-block caching") + cmd.Flags().String(CPUProfile, "", "Enable CPU profiling and write to the provided file") + cmd.Flags().Bool(sdkserver.FlagTrace, false, "Provide full stack traces for errors in ABCI Log") + cmd.Flags().String(sdkserver.FlagPruning, pruningtypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)") + cmd.Flags().Uint64(sdkserver.FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')") + cmd.Flags().Uint64(sdkserver.FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')") //nolint:lll + cmd.Flags().Uint(sdkserver.FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks") + cmd.Flags().Uint64(sdkserver.FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Tendermint blocks") + cmd.Flags().String(AppDBBackend, "", "The type of database for application and snapshots databases") + + cmd.Flags().Bool(GRPCOnly, false, "Start the node in gRPC query only mode without Tendermint process") + cmd.Flags().Bool(GRPCEnable, config.DefaultGRPCEnable, "Define if the gRPC server should be enabled") + cmd.Flags().String(GRPCAddress, serverconfig.DefaultGRPCAddress, "the gRPC server address to listen on") + cmd.Flags().Bool(GRPCWebEnable, config.DefaultGRPCWebEnable, "Define if the gRPC-Web server should be enabled. (Note: gRPC must also be enabled.)") + cmd.Flags().String(GRPCWebAddress, serverconfig.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on") + + cmd.Flags().Bool(RPCEnable, config.DefaultAPIEnable, "Defines if Cosmos-sdk REST server should be enabled") + cmd.Flags().Bool(EnabledUnsafeCors, false, "Defines if CORS should be enabled (unsafe - use it at your own risk)") + + cmd.Flags().Bool(JSONRPCEnable, config.DefaultJSONRPCEnable, "Define if the JSON-RPC server should be enabled") + cmd.Flags().StringSlice(JSONRPCAPI, config.GetDefaultAPINamespaces(), "Defines a list of JSON-RPC namespaces that should be enabled") + cmd.Flags().String(JSONRPCAddress, config.DefaultJSONRPCAddress, "the JSON-RPC server address to listen on") + cmd.Flags().String(JSONWsAddress, config.DefaultJSONRPCWsAddress, "the JSON-RPC WS server address to listen on") + cmd.Flags().Uint64(JSONRPCGasCap, config.DefaultEthCallGasLimit, "Sets a cap on gas that can be used in eth_call/estimateGas unit is unibi (0=infinite)") //nolint:lll + cmd.Flags().Float64(JSONRPCTxFeeCap, config.DefaultTxFeeCap, "Sets a cap on transaction fee that can be sent via the RPC APIs (1 = default 1 nibi)") //nolint:lll + cmd.Flags().Int32(JSONRPCFilterCap, config.DefaultFilterCap, "Sets the global cap for total number of filters that can be created") + cmd.Flags().Duration(JSONRPCEVMTimeout, config.DefaultEVMTimeout, "Sets a timeout used for eth_call (0=infinite)") + cmd.Flags().Duration(JSONRPCHTTPTimeout, config.DefaultHTTPTimeout, "Sets a read/write timeout for json-rpc http server (0=infinite)") + cmd.Flags().Duration(JSONRPCHTTPIdleTimeout, config.DefaultHTTPIdleTimeout, "Sets a idle timeout for json-rpc http server (0=infinite)") + cmd.Flags().Bool(JSONRPCAllowUnprotectedTxs, config.DefaultAllowUnprotectedTxs, "Allow for unprotected (non EIP155 signed) transactions to be submitted via the node's RPC when the global parameter is disabled") //nolint:lll + cmd.Flags().Int32(JSONRPCLogsCap, config.DefaultLogsCap, "Sets the max number of results can be returned from single `eth_getLogs` query") + cmd.Flags().Int32(JSONRPCBlockRangeCap, config.DefaultBlockRangeCap, "Sets the max block range allowed for `eth_getLogs` query") + cmd.Flags().Int(JSONRPCMaxOpenConnections, config.DefaultMaxOpenConnections, "Sets the maximum number of simultaneous connections for the server listener") //nolint:lll + cmd.Flags().Bool(JSONRPCEnableIndexer, false, "Enable the custom tx indexer for json-rpc") + cmd.Flags().Bool(JSONRPCEnableMetrics, false, "Define if EVM rpc metrics server should be enabled") + + cmd.Flags().String(EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)") //nolint:lll + cmd.Flags().Uint64(EVMMaxTxGasWanted, config.DefaultMaxTxGasWanted, "the gas wanted for each eth tx returned in ante handler in check tx mode") //nolint:lll + + cmd.Flags().String(TLSCertPath, "", "the cert.pem file path for the server TLS configuration") + cmd.Flags().String(TLSKeyPath, "", "the key.pem file path for the server TLS configuration") + + cmd.Flags().Uint64(sdkserver.FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval") + cmd.Flags().Uint32(sdkserver.FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep") + + // add support for all Tendermint-specific command line options + tcmd.AddNodeFlags(cmd) + return cmd +} + +func startStandAlone(ctx *sdkserver.Context, opts StartOptions) error { + addr := ctx.Viper.GetString(Address) + transport := ctx.Viper.GetString(Transport) + home := ctx.Viper.GetString(flags.FlagHome) + + db, err := openDB(home, sdkserver.GetAppDBBackend(ctx.Viper)) + if err != nil { + return err + } + + defer func() { + if err := db.Close(); err != nil { + ctx.Logger.Error("error closing db", "error", err.Error()) + } + }() + + traceWriterFile := ctx.Viper.GetString(TraceStore) + traceWriter, err := openTraceWriter(traceWriterFile) + if err != nil { + return err + } + + app := opts.AppCreator(ctx.Logger, db, traceWriter, ctx.Viper) + + conf, err := config.GetConfig(ctx.Viper) + if err != nil { + ctx.Logger.Error("failed to get server config", "error", err.Error()) + return err + } + + if err := conf.ValidateBasic(); err != nil { + ctx.Logger.Error("invalid server config", "error", err.Error()) + return err + } + + _, err = startTelemetry(conf) + if err != nil { + return err + } + + svr, err := abciserver.NewServer(addr, transport, app) + if err != nil { + return fmt.Errorf("error creating listener: %v", err) + } + + svr.SetLogger(ctx.Logger.With("server", "abci")) + + err = svr.Start() + if err != nil { + tmos.Exit(err.Error()) + } + + defer func() { + if err = svr.Stop(); err != nil { + tmos.Exit(err.Error()) + } + }() + + // Wait for SIGINT or SIGTERM signal + return sdkserver.WaitForQuitSignals() +} + +// legacyAminoCdc is used for the legacy REST API +func startInProcess(ctx *sdkserver.Context, clientCtx client.Context, opts StartOptions) (err error) { + cfg := ctx.Config + home := cfg.RootDir + logger := ctx.Logger + + db, err := openDB(home, sdkserver.GetAppDBBackend(ctx.Viper)) + if err != nil { + logger.Error("failed to open DB", "error", err.Error()) + return err + } + + defer func() { + if err := db.Close(); err != nil { + ctx.Logger.With("error", err).Error("error closing db") + } + }() + + traceWriterFile := ctx.Viper.GetString(TraceStore) + traceWriter, err := openTraceWriter(traceWriterFile) + if err != nil { + logger.Error("failed to open trace writer", "error", err.Error()) + return err + } + + conf, err := config.GetConfig(ctx.Viper) + if err != nil { + logger.Error("failed to get server config", "error", err.Error()) + return err + } + + if err := conf.ValidateBasic(); err != nil { + logger.Error("invalid server config", "error", err.Error()) + return err + } + + app := opts.AppCreator(ctx.Logger, db, traceWriter, ctx.Viper) + + nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) + if err != nil { + logger.Error("failed load or gen node key", "error", err.Error()) + return err + } + + genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) + + var ( + tmNode *node.Node + gRPCOnly = ctx.Viper.GetBool(GRPCOnly) + ) + + if gRPCOnly { + logger.Info("starting node in query only mode; Tendermint is disabled") + conf.GRPC.Enable = true + conf.JSONRPC.EnableIndexer = false + } else { + logger.Info("starting node with ABCI Tendermint in-process") + + tmNode, err = node.NewNode( + cfg, + pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()), + nodeKey, + proxy.NewLocalClientCreator(app), + genDocProvider, + node.DefaultDBProvider, + node.DefaultMetricsProvider(cfg.Instrumentation), + ctx.Logger.With("server", "node"), + ) + if err != nil { + logger.Error("failed init node", "error", err.Error()) + return err + } + + if err := tmNode.Start(); err != nil { + logger.Error("failed start tendermint server", "error", err.Error()) + return err + } + + defer func() { + if tmNode.IsRunning() { + _ = tmNode.Stop() + } + }() + } + + // Add the tx service to the gRPC router. We only need to register this + // service if API or gRPC or JSONRPC is enabled, and avoid doing so in the general + // case, because it spawns a new local tendermint RPC rpcClient. + if (conf.API.Enable || conf.GRPC.Enable || conf.JSONRPC.Enable || conf.JSONRPC.EnableIndexer) && tmNode != nil { + clientCtx = clientCtx.WithClient(local.New(tmNode)) + + app.RegisterTxService(clientCtx) + app.RegisterTendermintService(clientCtx) + app.RegisterNodeService(clientCtx) + } + + metrics, err := startTelemetry(conf) + if err != nil { + return err + } + + // Enable metrics if JSONRPC is enabled and --metrics is passed + // Flag not added in config to avoid user enabling in config without passing in CLI + if conf.JSONRPC.Enable && ctx.Viper.GetBool(JSONRPCEnableMetrics) { + ethmetricsexp.Setup(conf.JSONRPC.MetricsAddress) + } + + var evmIdxer eth.EVMTxIndexer + if conf.JSONRPC.EnableIndexer { + idxDB, err := OpenIndexerDB(home, sdkserver.GetAppDBBackend(ctx.Viper)) + if err != nil { + logger.Error("failed to open evm indexer DB", "error", err.Error()) + return err + } + evmTxIndexer, _, err := OpenEVMIndexer(ctx, idxDB, clientCtx) + if err != nil { + logger.Error("failed starting evm indexer service", "error", err.Error()) + return err + } + evmIdxer = evmTxIndexer + } + + if conf.API.Enable || conf.JSONRPC.Enable { + genDoc, err := genDocProvider() + if err != nil { + return err + } + + clientCtx = clientCtx. + WithHomeDir(home). + WithChainID(genDoc.ChainID) + + // Set `GRPCClient` to `clientCtx` to enjoy concurrent grpc query. + // only use it if gRPC server is enabled. + if conf.GRPC.Enable { + _, port, err := net.SplitHostPort(conf.GRPC.Address) + if err != nil { + return errorsmod.Wrapf(err, "invalid grpc address %s", conf.GRPC.Address) + } + + maxSendMsgSize := conf.GRPC.MaxSendMsgSize + if maxSendMsgSize == 0 { + maxSendMsgSize = serverconfig.DefaultGRPCMaxSendMsgSize + } + + maxRecvMsgSize := conf.GRPC.MaxRecvMsgSize + if maxRecvMsgSize == 0 { + maxRecvMsgSize = serverconfig.DefaultGRPCMaxRecvMsgSize + } + + grpcAddress := fmt.Sprintf("127.0.0.1:%s", port) + + // If grpc is enabled, configure grpc rpcClient for grpc gateway and json-rpc. + grpcClient, err := grpc.Dial( + grpcAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions( + grpc.ForceCodec(codec.NewProtoCodec(clientCtx.InterfaceRegistry).GRPCCodec()), + grpc.MaxCallRecvMsgSize(maxRecvMsgSize), + grpc.MaxCallSendMsgSize(maxSendMsgSize), + ), + ) + if err != nil { + return err + } + + clientCtx = clientCtx.WithGRPCClient(grpcClient) + ctx.Logger.Debug("gRPC rpcClient assigned to rpcClient context", "address", grpcAddress) + } + } + + var apiSrv *api.Server + if conf.API.Enable { + apiSrv = api.New(clientCtx, ctx.Logger.With("server", "api")) + app.RegisterAPIRoutes(apiSrv, conf.API) + + if conf.Telemetry.Enabled { + apiSrv.SetTelemetry(metrics) + } + + errCh := make(chan error) + go func() { + if err := apiSrv.Start(conf.Config); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-time.After(types.ServerStartTime): // assume server started successfully + } + + defer apiSrv.Close() + } + + var ( + grpcSrv *grpc.Server + grpcWebSrv *http.Server + ) + + if conf.GRPC.Enable { + grpcSrv, err = servergrpc.StartGRPCServer(clientCtx, app, conf.GRPC) + if err != nil { + return err + } + defer grpcSrv.Stop() + if conf.GRPCWeb.Enable { + grpcWebSrv, err = servergrpc.StartGRPCWeb(grpcSrv, conf.Config) + if err != nil { + ctx.Logger.Error("failed to start grpc-web http server", "error", err.Error()) + return err + } + + defer func() { + if err := grpcWebSrv.Close(); err != nil { + logger.Error("failed to close the grpc-web http server", "error", err.Error()) + } + }() + } + } + + var ( + httpSrv *http.Server + httpSrvDone chan struct{} + ) + + if conf.JSONRPC.Enable { + genDoc, err := genDocProvider() + if err != nil { + return err + } + + clientCtx := clientCtx.WithChainID(genDoc.ChainID) + + tmEndpoint := "/websocket" + tmRPCAddr := cfg.RPC.ListenAddress + httpSrv, httpSrvDone, err = StartJSONRPC( + ctx, clientCtx, tmRPCAddr, tmEndpoint, &conf, evmIdxer, + ) + if err != nil { + return err + } + defer func() { + shutdownCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second) + defer cancelFn() + if err := httpSrv.Shutdown(shutdownCtx); err != nil { + logger.Error("HTTP server shutdown produced a warning", "error", err.Error()) + } else { + logger.Info("HTTP server shut down, waiting 5 sec") + select { + case <-time.Tick(5 * time.Second): + case <-httpSrvDone: + } + } + }() + } + + // At this point it is safe to block the process if we're in query only mode as + // we do not need to start Rosetta or handle any Tendermint related processes. + if gRPCOnly { + // wait for signal capture and gracefully return + return sdkserver.WaitForQuitSignals() + } + + var rosettaSrv crgserver.Server + if conf.Rosetta.Enable { + offlineMode := conf.Rosetta.Offline + + // If GRPC is not enabled rosetta cannot work in online mode, so it works in + // offline mode. + if !conf.GRPC.Enable { + offlineMode = true + } + + minGasPrices, err := sdk.ParseDecCoins(conf.MinGasPrices) + if err != nil { + ctx.Logger.Error("failed to parse minimum-gas-prices", "error", err.Error()) + return err + } + + conf := &rosetta.Config{ + Blockchain: conf.Rosetta.Blockchain, + Network: conf.Rosetta.Network, + TendermintRPC: ctx.Config.RPC.ListenAddress, + GRPCEndpoint: conf.GRPC.Address, + Addr: conf.Rosetta.Address, + Retries: conf.Rosetta.Retries, + Offline: offlineMode, + GasToSuggest: conf.Rosetta.GasToSuggest, + EnableFeeSuggestion: conf.Rosetta.EnableFeeSuggestion, + GasPrices: minGasPrices.Sort(), + Codec: clientCtx.Codec.(*codec.ProtoCodec), + InterfaceRegistry: clientCtx.InterfaceRegistry, + } + + rosettaSrv, err = rosetta.ServerFromConfig(conf) + if err != nil { + return err + } + + errCh := make(chan error) + go func() { + if err := rosettaSrv.Start(); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-time.After(types.ServerStartTime): // assume server started successfully + } + } + // Wait for SIGINT or SIGTERM signal + return sdkserver.WaitForQuitSignals() +} + +// OpenIndexerDB opens the custom eth indexer db, using the same db backend as the main app +func OpenIndexerDB(rootDir string, backendType dbm.BackendType) (dbm.DB, error) { + dataDir := filepath.Join(rootDir, "data") + return dbm.NewDB("evmindexer", backendType, dataDir) +} + +func OpenEVMIndexer( + ctx *sdkserver.Context, indexerDb dbm.DB, clientCtx client.Context, +) (eth.EVMTxIndexer, *EVMTxIndexerService, error) { + idxLogger := ctx.Logger.With("indexer", "evm") + evmIndexer := indexer.NewEVMTxIndexer(indexerDb, idxLogger, clientCtx) + + evmIndexerService := NewEVMIndexerService(evmIndexer, clientCtx.Client.(rpcclient.Client)) + evmIndexerService.SetLogger(idxLogger) + + errCh := make(chan error) + go func() { + if err := evmIndexerService.Start(); err != nil { + errCh <- err + } + }() + select { + case err := <-errCh: + return nil, nil, err + case <-time.After(types.ServerStartTime): // assume server started successfully + } + return evmIndexer, evmIndexerService, nil +} + +func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { + if traceWriterFile == "" { + return + } + + filePath := filepath.Clean(traceWriterFile) + return os.OpenFile( + filePath, + os.O_WRONLY|os.O_APPEND|os.O_CREATE, + 0o600, + ) +} + +func startTelemetry(cfg config.Config) (*telemetry.Metrics, error) { + if !cfg.Telemetry.Enabled { + return nil, nil + } + return telemetry.New(cfg.Telemetry) +} + +// WaitForQuitSignals waits for SIGINT and SIGTERM and returns. +func WaitForQuitSignals() sdkserver.ErrorCode { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + sig := <-sigs + return sdkserver.ErrorCode{Code: int(sig.(syscall.Signal)) + 128} +} + +// wrapCPUProfile runs callback in a goroutine, then wait for quit signals. +func wrapCPUProfile(ctx *sdkserver.Context, callback func() error) error { + if cpuProfile := ctx.Viper.GetString(CPUProfile); cpuProfile != "" { + f, err := os.Create(cpuProfile) + if err != nil { + return err + } + + ctx.Logger.Info("starting CPU profiler", "profile", cpuProfile) + if err := pprof.StartCPUProfile(f); err != nil { + return err + } + + defer func() { + ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile) + pprof.StopCPUProfile() + if err := f.Close(); err != nil { + ctx.Logger.Info("failed to close cpu-profile file", "profile", cpuProfile, "err", err.Error()) + } + }() + } + + errCh := make(chan error) + go func() { + errCh <- callback() + }() + + select { + case err := <-errCh: + return err + + case <-time.After(types.ServerStartTime): + } + + return WaitForQuitSignals() +} diff --git a/app/server/util.go b/app/server/util.go new file mode 100644 index 000000000..9e45fe28b --- /dev/null +++ b/app/server/util.go @@ -0,0 +1,100 @@ +package server + +import ( + "net" + "time" + + "github.com/spf13/cobra" + "golang.org/x/net/netutil" + + srvconfig "github.com/NibiruChain/nibiru/v2/app/server/config" + + sdkserver "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/version" + + tmcmd "github.com/cometbft/cometbft/cmd/cometbft/commands" + tmlog "github.com/cometbft/cometbft/libs/log" + rpcclient "github.com/cometbft/cometbft/rpc/jsonrpc/client" +) + +// AddCommands adds server commands +func AddCommands( + rootCmd *cobra.Command, + opts StartOptions, + appExport types.AppExporter, + addStartFlags types.ModuleInitFlags, +) { + tendermintCmd := &cobra.Command{ + Use: "tendermint", + Short: "Tendermint subcommands", + } + + tendermintCmd.AddCommand( + sdkserver.ShowNodeIDCmd(), + sdkserver.ShowValidatorCmd(), + sdkserver.ShowAddressCmd(), + sdkserver.VersionCmd(), + tmcmd.ResetAllCmd, + tmcmd.ResetStateCmd, + sdkserver.BootstrapStateCmd(opts.AppCreator), + ) + + startCmd := StartCmd(opts) + addStartFlags(startCmd) + + rootCmd.AddCommand( + startCmd, + tendermintCmd, + sdkserver.ExportCmd(appExport, opts.DefaultNodeHome), + version.NewVersionCommand(), + sdkserver.NewRollbackCmd(opts.AppCreator, opts.DefaultNodeHome), + + // custom tx indexer command + // NewIndexTxCmd(), TODO: check indexer tx command + ) +} + +func ConnectTmWS(tmRPCAddr, tmEndpoint string, logger tmlog.Logger) *rpcclient.WSClient { + tmWsClient, err := rpcclient.NewWS(tmRPCAddr, tmEndpoint, + rpcclient.MaxReconnectAttempts(256), + rpcclient.ReadWait(120*time.Second), + rpcclient.WriteWait(120*time.Second), + rpcclient.PingPeriod(50*time.Second), + rpcclient.OnReconnect(func() { + logger.Debug("EVM RPC reconnects to Tendermint WS", "address", tmRPCAddr+tmEndpoint) + }), + ) + + if err != nil { + logger.Error( + "Tendermint WS rpcClient could not be created", + "address", tmRPCAddr+tmEndpoint, + "error", err, + ) + } else if err := tmWsClient.OnStart(); err != nil { + logger.Error( + "Tendermint WS rpcClient could not start", + "address", tmRPCAddr+tmEndpoint, + "error", err, + ) + } + + return tmWsClient +} + +// Listen starts a net.Listener on the tcp network on the given address. +// If there is a specified MaxOpenConnections in the config, it will also set the limitListener. +func Listen(addr string, config *srvconfig.Config) (net.Listener, error) { + if addr == "" { + addr = ":http" + } + ln, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + if config.JSONRPC.MaxOpenConnections > 0 { + ln = netutil.LimitListener(ln, config.JSONRPC.MaxOpenConnections) + } + return ln, err +} diff --git a/app/upgrades.go b/app/upgrades.go index ceda529e8..9cc83f588 100644 --- a/app/upgrades.go +++ b/app/upgrades.go @@ -3,18 +3,18 @@ package app import ( "fmt" - "github.com/NibiruChain/nibiru/app/upgrades/v1_0_3" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/NibiruChain/nibiru/app/upgrades" - "github.com/NibiruChain/nibiru/app/upgrades/v1_0_1" - "github.com/NibiruChain/nibiru/app/upgrades/v1_0_2" - "github.com/NibiruChain/nibiru/app/upgrades/v1_1_0" - "github.com/NibiruChain/nibiru/app/upgrades/v1_2_0" - "github.com/NibiruChain/nibiru/app/upgrades/v1_3_0" - "github.com/NibiruChain/nibiru/app/upgrades/v1_4_0" - "github.com/NibiruChain/nibiru/app/upgrades/v1_5_0" + "github.com/NibiruChain/nibiru/v2/app/upgrades" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v1_0_1" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v1_0_2" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v1_0_3" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v1_1_0" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v1_2_0" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v1_3_0" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v1_4_0" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v1_5_0" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v2_0_0" ) var Upgrades = []upgrades.Upgrade{ @@ -26,6 +26,7 @@ var Upgrades = []upgrades.Upgrade{ v1_3_0.Upgrade, v1_4_0.Upgrade, v1_5_0.Upgrade, + v2_0_0.Upgrade, } func (app *NibiruApp) setupUpgrades() { @@ -35,7 +36,7 @@ func (app *NibiruApp) setupUpgrades() { func (app *NibiruApp) setUpgradeHandlers() { for _, u := range Upgrades { - app.upgradeKeeper.SetUpgradeHandler(u.UpgradeName, u.CreateUpgradeHandler(app.mm, app.configurator)) + app.upgradeKeeper.SetUpgradeHandler(u.UpgradeName, u.CreateUpgradeHandler(app.ModuleManager, app.configurator)) } } diff --git a/app/upgrades/v1_0_1/constants.go b/app/upgrades/v1_0_1/constants.go index 79444f0b5..1548161da 100644 --- a/app/upgrades/v1_0_1/constants.go +++ b/app/upgrades/v1_0_1/constants.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/NibiruChain/nibiru/app/upgrades" + "github.com/NibiruChain/nibiru/v2/app/upgrades" ) const UpgradeName = "v1.0.1" diff --git a/app/upgrades/v1_0_2/constants.go b/app/upgrades/v1_0_2/constants.go index 8b878729c..5333497fc 100644 --- a/app/upgrades/v1_0_2/constants.go +++ b/app/upgrades/v1_0_2/constants.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/NibiruChain/nibiru/app/upgrades" + "github.com/NibiruChain/nibiru/v2/app/upgrades" ) const UpgradeName = "v1.0.2" diff --git a/app/upgrades/v1_0_3/constants.go b/app/upgrades/v1_0_3/constants.go index 3ec54ae30..acec556b6 100644 --- a/app/upgrades/v1_0_3/constants.go +++ b/app/upgrades/v1_0_3/constants.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/NibiruChain/nibiru/app/upgrades" + "github.com/NibiruChain/nibiru/v2/app/upgrades" ) const UpgradeName = "v1.0.3" diff --git a/app/upgrades/v1_1_0/constants.go b/app/upgrades/v1_1_0/constants.go index 257ef77c5..4fdebdf6c 100644 --- a/app/upgrades/v1_1_0/constants.go +++ b/app/upgrades/v1_1_0/constants.go @@ -6,8 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/NibiruChain/nibiru/app/upgrades" - inflationtypes "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/app/upgrades" + inflationtypes "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) const UpgradeName = "v1.1.0" diff --git a/app/upgrades/v1_2_0/constants.go b/app/upgrades/v1_2_0/constants.go index fea9289a7..63718c21d 100644 --- a/app/upgrades/v1_2_0/constants.go +++ b/app/upgrades/v1_2_0/constants.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/NibiruChain/nibiru/app/upgrades" + "github.com/NibiruChain/nibiru/v2/app/upgrades" ) const UpgradeName = "v1.2.0" diff --git a/app/upgrades/v1_3_0/constants.go b/app/upgrades/v1_3_0/constants.go index 4788dbdd6..320bdae7c 100644 --- a/app/upgrades/v1_3_0/constants.go +++ b/app/upgrades/v1_3_0/constants.go @@ -15,7 +15,7 @@ import ( icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - "github.com/NibiruChain/nibiru/app/upgrades" + "github.com/NibiruChain/nibiru/v2/app/upgrades" ) const UpgradeName = "v1.3.0" diff --git a/app/upgrades/v1_4_0/constants.go b/app/upgrades/v1_4_0/constants.go index 5043704eb..fbfa766ea 100644 --- a/app/upgrades/v1_4_0/constants.go +++ b/app/upgrades/v1_4_0/constants.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/NibiruChain/nibiru/app/upgrades" + "github.com/NibiruChain/nibiru/v2/app/upgrades" ) const UpgradeName = "v1.4.0" diff --git a/app/upgrades/v1_5_0/constants.go b/app/upgrades/v1_5_0/constants.go index 34ec94308..71e4aa827 100644 --- a/app/upgrades/v1_5_0/constants.go +++ b/app/upgrades/v1_5_0/constants.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - "github.com/NibiruChain/nibiru/app/upgrades" + "github.com/NibiruChain/nibiru/v2/app/upgrades" ) const UpgradeName = "v1.5.0" diff --git a/app/upgrades/v2_0_0/constants.go b/app/upgrades/v2_0_0/constants.go new file mode 100644 index 000000000..cc4c5eaf2 --- /dev/null +++ b/app/upgrades/v2_0_0/constants.go @@ -0,0 +1,25 @@ +package v2_0_0 + +import ( + "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + "github.com/NibiruChain/nibiru/v2/app/upgrades" + evmtypes "github.com/NibiruChain/nibiru/v2/x/evm" +) + +const UpgradeName = "v2.0.0" + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: func(mm *module.Manager, cfg module.Configurator) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + return mm.RunMigrations(ctx, cfg, fromVM) + } + }, + StoreUpgrades: types.StoreUpgrades{ + Added: []string{evmtypes.ModuleName}, + }, +} diff --git a/wasmbinding/stargate_query.go b/app/wasmext/stargate_query.go similarity index 86% rename from wasmbinding/stargate_query.go rename to app/wasmext/stargate_query.go index 059f777cf..2d90b6d58 100644 --- a/wasmbinding/stargate_query.go +++ b/app/wasmext/stargate_query.go @@ -1,13 +1,14 @@ -package wasmbinding +package wasmext import ( wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - devgas "github.com/NibiruChain/nibiru/x/devgas/v1/types" - epochs "github.com/NibiruChain/nibiru/x/epochs/types" - oracle "github.com/NibiruChain/nibiru/x/oracle/types" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" - tokenfactory "github.com/NibiruChain/nibiru/x/tokenfactory/types" + devgas "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" + epochs "github.com/NibiruChain/nibiru/v2/x/epochs/types" + inflation "github.com/NibiruChain/nibiru/v2/x/inflation/types" + oracle "github.com/NibiruChain/nibiru/v2/x/oracle/types" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" + tokenfactory "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" auth "github.com/cosmos/cosmos-sdk/x/auth/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -109,14 +110,13 @@ func WasmAcceptedStargateQueries() wasmkeeper.AcceptedStargateQueries { "/nibiru.epochs.v1.Query/EpochInfos": new(epochs.QueryEpochInfosResponse), "/nibiru.epochs.v1.Query/CurrentEpoch": new(epochs.QueryCurrentEpochResponse), - // TODO: for post v1 // nibiru inflation - // "/nibiru.inflation.v1.Query/Period": new(inflation.QueryPeriodResponse), - // "/nibiru.inflation.v1.Query/EpochMintProvision": new(inflation.QueryEpochMintProvisionResponse), - // "/nibiru.inflation.v1.Query/SkippedEpochs": new(inflation.QuerySkippedEpochsResponse), - // "/nibiru.inflation.v1.Query/CirculatingSupply": new(inflation.QueryCirculatingSupplyResponse), - // "/nibiru.inflation.v1.Query/InflationRate": new(inflation.QueryInflationRateResponse), - // "/nibiru.inflation.v1.Query/Params": new(inflation.QueryParamsResponse), + "/nibiru.inflation.v1.Query/Period": new(inflation.QueryPeriodResponse), + "/nibiru.inflation.v1.Query/EpochMintProvision": new(inflation.QueryEpochMintProvisionResponse), + "/nibiru.inflation.v1.Query/SkippedEpochs": new(inflation.QuerySkippedEpochsResponse), + "/nibiru.inflation.v1.Query/CirculatingSupply": new(inflation.QueryCirculatingSupplyResponse), + "/nibiru.inflation.v1.Query/InflationRate": new(inflation.QueryInflationRateResponse), + "/nibiru.inflation.v1.Query/Params": new(inflation.QueryParamsResponse), // nibiru oracle "/nibiru.oracle.v1.Query/ExchangeRate": new(oracle.QueryExchangeRateResponse), @@ -140,11 +140,5 @@ func WasmAcceptedStargateQueries() wasmkeeper.AcceptedStargateQueries { "/nibiru.devgas.v1.Query/FeeShare": new(devgas.QueryFeeShareResponse), "/nibiru.devgas.v1.Query/Params": new(devgas.QueryParamsResponse), "/nibiru.devgas.v1.Query/FeeSharesByWithdrawer": new(devgas.QueryFeeSharesByWithdrawerResponse), - - // TODO: for post v1 - // nibiru.perp - - // TODO: for post v1 - // nibiru.spot } } diff --git a/wasmbinding/stargate_query_test.go b/app/wasmext/stargate_query_test.go similarity index 87% rename from wasmbinding/stargate_query_test.go rename to app/wasmext/stargate_query_test.go index 3a06723f3..1b8f981f1 100644 --- a/wasmbinding/stargate_query_test.go +++ b/app/wasmext/stargate_query_test.go @@ -1,23 +1,23 @@ -package wasmbinding_test +package wasmext_test import ( "fmt" "strings" - "testing" "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/assert" "google.golang.org/grpc" - "github.com/NibiruChain/nibiru/wasmbinding" + wasmbinding "github.com/NibiruChain/nibiru/v2/app/wasmext" - "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/set" - devgas "github.com/NibiruChain/nibiru/x/devgas/v1/types" - epochs "github.com/NibiruChain/nibiru/x/epochs/types" - oracle "github.com/NibiruChain/nibiru/x/oracle/types" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" - tokenfactory "github.com/NibiruChain/nibiru/x/tokenfactory/types" + devgas "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" + epochs "github.com/NibiruChain/nibiru/v2/x/epochs/types" + inflation "github.com/NibiruChain/nibiru/v2/x/inflation/types" + oracle "github.com/NibiruChain/nibiru/v2/x/oracle/types" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" + tokenfactory "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) /* @@ -35,11 +35,13 @@ Given only the `PB_MSG.PACKAGE` and the `PB_MSG.NAME` of either the query request or response, we should know the `QueryRequest::Stargate.path` deterministically. */ -func TestWasmAcceptedStargateQueries(t *testing.T) { +func (s *Suite) TestWasmAcceptedStargateQueries() { + t := s.T() t.Log("stargateQueryPaths: Add nibiru query paths from GRPC service descriptions") queryServiceDescriptions := []grpc.ServiceDesc{ epochs.GrpcQueryServiceDesc(), devgas.GrpcQueryServiceDesc(), + inflation.GrpcQueryServiceDesc(), oracle.GrpcQueryServiceDesc(), sudotypes.GrpcQueryServiceDesc(), tokenfactory.GrpcQueryServiceDesc(), diff --git a/app/wasmext/wasm.go b/app/wasmext/wasm.go new file mode 100644 index 000000000..6ccfa2329 --- /dev/null +++ b/app/wasmext/wasm.go @@ -0,0 +1,138 @@ +package wasmext + +import ( + "github.com/NibiruChain/nibiru/v2/x/evm" + + "cosmossdk.io/errors" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasm "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + sdkcodec "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// NibiruWasmOptions: Wasm Options are extension points to instantiate the Wasm +// keeper with non-default values +func NibiruWasmOptions( + grpcQueryRouter *baseapp.GRPCQueryRouter, + appCodec codec.Codec, + msgHandlerArgs MsgHandlerArgs, +) []wasmkeeper.Option { + wasmQueryOption := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ + Stargate: wasmkeeper.AcceptListStargateQuerier( + WasmAcceptedStargateQueries(), + grpcQueryRouter, + appCodec, + ), + }) + + wasmMsgHandlerOption := wasmkeeper.WithMessageHandler(WasmMessageHandler(msgHandlerArgs)) + + return []wasmkeeper.Option{ + wasmQueryOption, + wasmMsgHandlerOption, + } +} + +func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Address, msg sdk.Msg) (*sdk.Result, error) { + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + // make sure this account can send it + for _, acct := range msg.GetSigners() { + if !acct.Equals(contractAddr) { + return nil, errors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission") + } + } + + msgTypeUrl := sdk.MsgTypeURL(msg) + if msgTypeUrl == sdk.MsgTypeURL(new(evm.MsgEthereumTx)) { + return nil, errors.Wrap(sdkerrors.ErrUnauthorized, "Wasm VM to EVM call pattern is not yet supported") + } + + // find the handler and execute it + if handler := h.router.Handler(msg); handler != nil { + // ADR 031 request type routing + msgResult, err := handler(ctx, msg) + return msgResult, err + } + // legacy sdk.Msg routing + // Assuming that the app developer has migrated all their Msgs to + // proto messages and has registered all `Msg services`, then this + // path should never be called, because all those Msgs should be + // registered within the `msgServiceRouter` already. + return nil, errors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg) +} + +type MsgHandlerArgs struct { + Router MessageRouter + Ics4Wrapper wasm.ICS4Wrapper + ChannelKeeper wasm.ChannelKeeper + CapabilityKeeper wasm.CapabilityKeeper + BankKeeper wasm.Burner + Unpacker sdkcodec.AnyUnpacker + PortSource wasm.ICS20TransferPortSource +} + +// SDKMessageHandler can handles messages that can be encoded into sdk.Message types and routed. +type SDKMessageHandler struct { + router MessageRouter + encoders msgEncoder +} + +// MessageRouter ADR 031 request type routing +type MessageRouter interface { + Handler(msg sdk.Msg) baseapp.MsgServiceHandler +} + +// msgEncoder is an extension point to customize encodings +type msgEncoder interface { + // Encode converts wasmvm message to n cosmos message types + Encode(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Msg, error) +} + +// WasmMessageHandler is a replacement constructor for +// [wasmkeeper.NewDefaultMessageHandler] inside of [wasmkeeper.NewKeeper]. +func WasmMessageHandler( + args MsgHandlerArgs, +) wasmkeeper.Messenger { + encoders := wasmkeeper.DefaultEncoders(args.Unpacker, args.PortSource) + return wasmkeeper.NewMessageHandlerChain( + NewSDKMessageHandler(args.Router, encoders), + wasmkeeper.NewIBCRawPacketHandler(args.Ics4Wrapper, args.ChannelKeeper, args.CapabilityKeeper), + wasmkeeper.NewBurnCoinMessageHandler(args.BankKeeper), + ) +} + +func NewSDKMessageHandler(router MessageRouter, encoders msgEncoder) SDKMessageHandler { + return SDKMessageHandler{ + router: router, + encoders: encoders, + } +} + +func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { + sdkMsgs, err := h.encoders.Encode(ctx, contractAddr, contractIBCPortID, msg) + if err != nil { + return nil, nil, err + } + for _, sdkMsg := range sdkMsgs { + res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg) + if err != nil { + return nil, nil, err + } + // append data + data = append(data, res.Data) + // append events + sdkEvents := make([]sdk.Event, len(res.Events)) + for i := range res.Events { + sdkEvents[i] = sdk.Event(res.Events[i]) + } + events = append(events, sdkEvents...) + } + return +} diff --git a/app/wasmext/wasm_cli_test/cli_test.go b/app/wasmext/wasm_cli_test/cli_test.go new file mode 100644 index 000000000..59ecc202f --- /dev/null +++ b/app/wasmext/wasm_cli_test/cli_test.go @@ -0,0 +1,146 @@ +package wasm_cli_test + +import ( + "encoding/hex" + "fmt" + "testing" + + wasmcli "github.com/CosmWasm/wasmd/x/wasm/client/cli" + + "cosmossdk.io/math" + + "github.com/CosmWasm/wasmd/x/wasm/types" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" +) + +// commonArgs is args for CLI test commands. +var commonArgs = []string{ + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), + fmt.Sprintf("--%s=%s", flags.FlagFees, + sdk.NewCoins(sdk.NewCoin(denoms.NIBI, math.NewInt(10_000_000))).String()), +} + +var _ suite.TearDownAllSuite = (*TestSuite)(nil) + +type TestSuite struct { + suite.Suite + + cfg testnetwork.Config + network *testnetwork.Network +} + +func (s *TestSuite) SetupSuite() { + testutil.BeforeIntegrationSuite(s.T()) + testapp.EnsureNibiruPrefix() + + encodingConfig := app.MakeEncodingConfig() + genesisState := genesis.NewTestGenesisState(encodingConfig) + s.cfg = testnetwork.BuildNetworkConfig(genesisState) + network, err := testnetwork.New(s.T(), s.T().TempDir(), s.cfg) + s.Require().NoError(err) + + s.network = network + s.Require().NoError(s.network.WaitForNextBlock()) +} + +func (s *TestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *TestSuite) TestWasmHappyPath() { + s.requiredDeployedContractsLen(0) + + _, err := s.deployWasmContract("testdata/cw_nameservice.wasm") + s.Require().NoError(err) + + err = s.network.WaitForNextBlock() + s.Require().NoError(err) + + s.requiredDeployedContractsLen(1) +} + +// deployWasmContract deploys a wasm contract located in path. +func (s *TestSuite) deployWasmContract(path string) (uint64, error) { + val := s.network.Validators[0] + codec := val.ClientCtx.Codec + + args := []string{ + path, + "--from", val.Address.String(), + "--gas", "11000000", + } + args = append(args, commonArgs...) + + cmd := wasmcli.StoreCodeCmd() + out, err := cli.ExecTestCLICmd(val.ClientCtx, cmd, args) + if err != nil { + return 0, err + } + s.Require().NoError(s.network.WaitForNextBlock()) + + resp := &sdk.TxResponse{} + err = codec.UnmarshalJSON(out.Bytes(), resp) + if err != nil { + return 0, err + } + + resp, err = testnetwork.QueryTx(val.ClientCtx, resp.TxHash) + if err != nil { + return 0, err + } + + decodedResult, err := hex.DecodeString(resp.Data) + if err != nil { + return 0, err + } + + respData := sdk.TxMsgData{} + err = codec.Unmarshal(decodedResult, &respData) + if err != nil { + return 0, err + } + + if len(respData.MsgResponses) < 1 { + return 0, fmt.Errorf("no data found in response") + } + + var storeCodeResponse types.MsgStoreCodeResponse + err = codec.Unmarshal(respData.MsgResponses[0].Value, &storeCodeResponse) + if err != nil { + return 0, err + } + + return storeCodeResponse.CodeID, nil +} + +// requiredDeployedContractsLen checks the number of deployed contracts. +func (s *TestSuite) requiredDeployedContractsLen(total int) { + val := s.network.Validators[0] + var queryCodeResponse types.QueryCodesResponse + err := testnetwork.ExecQuery( + val.ClientCtx, + wasmcli.GetCmdListCode(), + []string{}, + &queryCodeResponse, + ) + s.Require().NoError(err) + s.Require().Len(queryCodeResponse.CodeInfos, total) +} + +func TestIntegrationTestSuite(t *testing.T) { + testutil.RetrySuiteRunIfDbClosed(t, func() { + suite.Run(t, new(TestSuite)) + }, 2) +} diff --git a/app/wasmext/wasm_cli_test/testdata/cw_nameservice.wasm b/app/wasmext/wasm_cli_test/testdata/cw_nameservice.wasm new file mode 100644 index 000000000..24b39ec52 Binary files /dev/null and b/app/wasmext/wasm_cli_test/testdata/cw_nameservice.wasm differ diff --git a/app/wasmext/wasmext_test.go b/app/wasmext/wasmext_test.go new file mode 100644 index 000000000..7788633d5 --- /dev/null +++ b/app/wasmext/wasmext_test.go @@ -0,0 +1,98 @@ +package wasmext_test + +import ( + "math/big" + "testing" + + wasmvm "github.com/CosmWasm/wasmvm/types" + sdkcodec "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/app/wasmext" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +type Suite struct { + suite.Suite +} + +func TestWasmExtSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} + +// WasmVM to EVM call pattern is not yet supported. This test verifies the +// Nibiru's [wasmkeeper.Option] function as expected. +func (s *Suite) TestEvmFilter() { + deps := evmtest.NewTestDeps() + // wk := wasmkeeper.NewDefaultPermissionKeeper(deps.App.WasmKeeper) + wasmMsgHandler := wasmext.WasmMessageHandler(deps.App.WasmMsgHandlerArgs) + + s.T().Log("Create a valid Ethereum tx msg") + + to := evmtest.NewEthPrivAcc() + ethTxMsg, err := evmtest.TxTransferWei{ + Deps: &deps, + To: to.EthAddr, + AmountWei: evm.NativeToWei(big.NewInt(420)), + }.Build() + s.NoError(err) + + s.T().Log("Validate Eth tx msg proto encoding as wasmvm.StargateMsg") + wasmContractAddr := deps.Sender.NibiruAddr + protoValueBz, err := deps.EncCfg.Codec.Marshal(ethTxMsg) + s.Require().NoError(err, "expect ethTxMsg to proto marshal", protoValueBz) + + _, ok := deps.EncCfg.Codec.(sdkcodec.AnyUnpacker) + s.Require().True(ok, "codec must be an AnyUnpacker") + + pbAny, err := sdkcodec.NewAnyWithValue(ethTxMsg) + s.NoError(err) + pbAnyBz, err := pbAny.Marshal() + s.NoError(err, pbAnyBz) + + var sdkMsg sdk.Msg + err = deps.EncCfg.Codec.UnpackAny(pbAny, &sdkMsg) + s.Require().NoError(err) + s.Equal("/eth.evm.v1.MsgEthereumTx", sdk.MsgTypeURL(sdkMsg)) + + s.T().Log("Dispatch the Eth tx msg from Wasm (unsuccessfully)") + _, _, err = wasmMsgHandler.DispatchMsg( + deps.Ctx, + wasmContractAddr, + "ibcport-unused", + wasmvm.CosmosMsg{ + Stargate: &wasmvm.StargateMsg{ + TypeURL: sdk.MsgTypeURL(ethTxMsg), + Value: protoValueBz, + }, + }, + ) + s.Require().ErrorContains(err, "Wasm VM to EVM call pattern is not yet supported") + + coins := sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 420)) // arbitrary constant + err = testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, coins) + s.NoError(err) + txMsg := &bank.MsgSend{ + FromAddress: deps.Sender.NibiruAddr.String(), + ToAddress: evmtest.NewEthPrivAcc().NibiruAddr.String(), + Amount: []sdk.Coin{sdk.NewInt64Coin(evm.EVMBankDenom, 20)}, + } + protoValueBz, err = deps.EncCfg.Codec.Marshal(txMsg) + s.NoError(err) + _, _, err = wasmMsgHandler.DispatchMsg( + deps.Ctx, + wasmContractAddr, + "ibcport-unused", + wasmvm.CosmosMsg{ + Stargate: &wasmvm.StargateMsg{ + TypeURL: sdk.MsgTypeURL(txMsg), + Value: protoValueBz, + }, + }, + ) + s.Require().NoError(err) +} diff --git a/cmd/nibid/cmd/base64.go b/cmd/nibid/cmd/base64.go index 5f235d3ea..ba5685aff 100644 --- a/cmd/nibid/cmd/base64.go +++ b/cmd/nibid/cmd/base64.go @@ -33,7 +33,7 @@ func GetBuildWasmMsg() *cobra.Command { Value string `json:"value,omitempty"` } - js, err := json.Marshal(map[string]interface{}{ + js, err := json.Marshal(map[string]any{ "stargate": stargateMessage{ TypeURL: anyMsg.TypeUrl, Value: base64.StdEncoding.EncodeToString(anyMsg.Value), diff --git a/cmd/nibid/cmd/decode_base64.go b/cmd/nibid/cmd/decode_base64.go index 3de3706f2..8625070c3 100644 --- a/cmd/nibid/cmd/decode_base64.go +++ b/cmd/nibid/cmd/decode_base64.go @@ -27,7 +27,7 @@ import ( // from the provided JSON data. // - err error: An error object, which is nil if the operation is successful. func YieldStargateMsgs(jsonBz []byte) (sgMsgs []wasmvm.StargateMsg, err error) { - var data interface{} + var data any if err := json.Unmarshal(jsonBz, &data); err != nil { return sgMsgs, err } @@ -50,7 +50,7 @@ func YieldStargateMsgs(jsonBz []byte) (sgMsgs []wasmvm.StargateMsg, err error) { // encoded base 64 string. func parseStargateMsgs(jsonData any, msgs *[]wasmvm.StargateMsg) { switch v := jsonData.(type) { - case map[string]interface{}: + case map[string]any: if typeURL, ok := v["type_url"].(string); ok { if value, ok := v["value"].(string); ok { *msgs = append(*msgs, wasmvm.StargateMsg{ @@ -62,7 +62,7 @@ func parseStargateMsgs(jsonData any, msgs *[]wasmvm.StargateMsg) { for _, value := range v { parseStargateMsgs(value, msgs) } - case []interface{}: + case []any: for _, value := range v { parseStargateMsgs(value, msgs) } @@ -93,7 +93,7 @@ func DecodeBase64StargateMsgs( ) (newSgMsgs []StargateMsgDecoded, err error) { codec := clientCtx.Codec - var data interface{} + var data any if err := json.Unmarshal(jsonBz, &data); err != nil { return newSgMsgs, fmt.Errorf( "failed to decode stargate msgs due to invalid JSON: %w", err) diff --git a/cmd/nibid/cmd/decode_base64_test.go b/cmd/nibid/cmd/decode_base64_test.go index c5e3c1e4e..96c136608 100644 --- a/cmd/nibid/cmd/decode_base64_test.go +++ b/cmd/nibid/cmd/decode_base64_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/v2/app" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/client" @@ -13,7 +13,7 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" - nibid "github.com/NibiruChain/nibiru/cmd/nibid/cmd" + nibid "github.com/NibiruChain/nibiru/v2/cmd/nibid/cmd" ) func TestBase64Decode(t *testing.T) { @@ -31,7 +31,7 @@ func TestBase64Decode(t *testing.T) { cfg, err := genutiltest.CreateDefaultTendermintConfig(home) require.NoError(t, err) - appCodec := app.MakeEncodingConfig().Marshaler + appCodec := app.MakeEncodingConfig().Codec err = genutiltest.ExecInitCmd( testModuleBasicManager, home, appCodec) require.NoError(t, err) diff --git a/cmd/nibid/cmd/genaccounts.go b/cmd/nibid/cmd/genaccounts.go index 6b79b1d87..53fca441e 100644 --- a/cmd/nibid/cmd/genaccounts.go +++ b/cmd/nibid/cmd/genaccounts.go @@ -3,9 +3,9 @@ package cmd import ( "bufio" "encoding/json" - "errors" "fmt" + "github.com/MakeNowJust/heredoc/v2" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -14,28 +14,21 @@ import ( "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) -const ( - flagVestingStart = "vesting-start-time" - flagVestingEnd = "vesting-end-time" - flagVestingAmt = "vesting-amount" -) - // AddGenesisAccountCmd returns add-genesis-account cobra Command. func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command { cmd := &cobra.Command{ Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", Short: "Add a genesis account to genesis.json", - Long: `Add a genesis account to genesis.json. The provided account must specify + Long: heredoc.Doc(`Add a genesis account to genesis.json. The provided account must specify the account address or key name and a list of initial coins. If a key name is given, the address will be looked up in the local Keybase. The list of initial tokens must -contain valid denominations. Accounts may optionally be supplied with vesting parameters. -`, +contain valid denominations. +`), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx := client.GetClientContextFromCmd(cmd) @@ -74,42 +67,11 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa return fmt.Errorf("failed to parse coins: %w", err) } - vestingStart, _ := cmd.Flags().GetInt64(flagVestingStart) - vestingEnd, _ := cmd.Flags().GetInt64(flagVestingEnd) - vestingAmtStr, _ := cmd.Flags().GetString(flagVestingAmt) - - vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr) - if err != nil { - return fmt.Errorf("failed to parse vesting amount: %w", err) - } - // create concrete account type based on input parameters var genAccount authtypes.GenesisAccount balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()} - baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0) - - if !vestingAmt.IsZero() { - baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) - - if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || - baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) { - return errors.New("vesting amount cannot be greater than total amount") - } - - switch { - case vestingStart != 0 && vestingEnd != 0: - genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart) - - case vestingEnd != 0: - genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount) - - default: - return errors.New("invalid vesting parameters; must supply start and end time or end time") - } - } else { - genAccount = baseAccount - } + genAccount = authtypes.NewBaseAccount(addr, nil, 0, 0) if err := genAccount.Validate(); err != nil { return fmt.Errorf("failed to validate new genesis account: %w", err) @@ -174,9 +136,6 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") - cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") - cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") - cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") flags.AddQueryFlagsToCmd(cmd) return cmd diff --git a/cmd/nibid/cmd/genaccounts_test.go b/cmd/nibid/cmd/genaccounts_test.go index 3846fc2f3..060f0ce4d 100644 --- a/cmd/nibid/cmd/genaccounts_test.go +++ b/cmd/nibid/cmd/genaccounts_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/v2/app" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/client" @@ -17,9 +17,9 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" - nibid "github.com/NibiruChain/nibiru/cmd/nibid/cmd" + nibid "github.com/NibiruChain/nibiru/v2/cmd/nibid/cmd" ) var testModuleBasicManager = module.NewBasicManager(genutil.AppModuleBasic{}) @@ -41,7 +41,7 @@ func TestAddGenesisAccountCmd(t *testing.T) { cfg, err := genutiltest.CreateDefaultTendermintConfig(home) require.NoError(t, err) - appCodec := app.MakeEncodingConfig().Marshaler + appCodec := app.MakeEncodingConfig().Codec err = genutiltest.ExecInitCmd( testModuleBasicManager, home, appCodec) require.NoError(t, err) diff --git a/cmd/nibid/cmd/init.go b/cmd/nibid/cmd/init.go index cc8295bcf..1d8c439a9 100644 --- a/cmd/nibid/cmd/init.go +++ b/cmd/nibid/cmd/init.go @@ -6,11 +6,11 @@ import ( "fmt" "os" "path/filepath" - "time" - db "github.com/cometbft/cometbft-db" tmcfg "github.com/cometbft/cometbft/config" + "github.com/NibiruChain/nibiru/v2/app/appconst" + tmcli "github.com/cometbft/cometbft/libs/cli" tmrand "github.com/cometbft/cometbft/libs/rand" tmtypes "github.com/cometbft/cometbft/types" @@ -38,25 +38,6 @@ const ( FlagDefaultBondDenom = "default-denom" ) -func customTendermintConfig() *tmcfg.Config { - cfg := tmcfg.DefaultConfig() - - // Overwrite consensus config - ms := func(n time.Duration) time.Duration { - return n * time.Millisecond - } - cfg.Consensus.TimeoutPropose = ms(3_000) - cfg.Consensus.TimeoutProposeDelta = ms(500) - cfg.Consensus.TimeoutPrevote = ms(1_000) - cfg.Consensus.TimeoutPrevoteDelta = ms(500) - cfg.Consensus.TimeoutPrecommit = ms(1_000) - cfg.Consensus.TimeoutPrecommitDelta = ms(500) - cfg.Consensus.TimeoutCommit = ms(1_000) - - cfg.DBBackend = string(db.PebbleDBBackend) - return cfg -} - /* InitCmd is a stand-in replacement for genutilcli.InitCmd that overwrites the consensus configutation in the `config.toml` prior to writing it to the disk. @@ -176,7 +157,7 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command { toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState) - customCfg := customTendermintConfig() + customCfg := appconst.NewDefaultTendermintConfig() config.Consensus = customCfg.Consensus config.DBBackend = customCfg.DBBackend tmcfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) diff --git a/cmd/nibid/cmd/root.go b/cmd/nibid/cmd/root.go index 7e67c2610..d52d70401 100644 --- a/cmd/nibid/cmd/root.go +++ b/cmd/nibid/cmd/root.go @@ -5,7 +5,11 @@ import ( "io" "os" - "github.com/NibiruChain/nibiru/x/sudo/cli" + "github.com/NibiruChain/nibiru/v2/app/server" + srvconfig "github.com/NibiruChain/nibiru/v2/app/server/config" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/x/sudo/cli" dbm "github.com/cometbft/cometbft-db" tmcli "github.com/cometbft/cometbft/libs/cli" @@ -17,8 +21,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/pruning" "github.com/cosmos/cosmos-sdk/client/rpc" - "github.com/cosmos/cosmos-sdk/server" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" + sdkserver "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -27,18 +30,18 @@ import ( genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/app" - oraclecli "github.com/NibiruChain/nibiru/x/oracle/client/cli" + "github.com/NibiruChain/nibiru/v2/app" + oraclecli "github.com/NibiruChain/nibiru/v2/x/oracle/cli" ) // NewRootCmd creates a new root command for nibid. It is called once in the // main function. func NewRootCmd() (*cobra.Command, app.EncodingConfig) { encodingConfig := app.MakeEncodingConfig() - app.SetPrefixes(app.AccountAddressPrefix) + app.SetPrefixes(appconst.AccountAddressPrefix) initClientCtx := client.Context{}. - WithCodec(encodingConfig.Marshaler). + WithCodec(encodingConfig.Codec). WithInterfaceRegistry(encodingConfig.InterfaceRegistry). WithTxConfig(encodingConfig.TxConfig). WithLegacyAmino(encodingConfig.Amino). @@ -48,8 +51,9 @@ func NewRootCmd() (*cobra.Command, app.EncodingConfig) { WithViper("") // In simapp, we don't use any prefix for env variables. rootCmd := &cobra.Command{ - Use: "nibid", - Short: "Nibiru app", + Use: "nibid", + Short: "Nibiru blockchain node CLI", + Aliases: []string{"nibiru"}, PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { cmd.SetOut(cmd.OutOrStdout()) cmd.SetErr(cmd.ErrOrStderr()) @@ -71,10 +75,10 @@ func NewRootCmd() (*cobra.Command, app.EncodingConfig) { return err } - customAppTemplate, customAppConfig := initAppConfig() - tmCfg := customTendermintConfig() + customAppTemplate, customAppConfig := srvconfig.AppConfig("unibi") + tmCfg := appconst.NewDefaultTendermintConfig() - return server.InterceptConfigsPreRunHandler( + return sdkserver.InterceptConfigsPreRunHandler( cmd, customAppTemplate, customAppConfig, @@ -88,39 +92,6 @@ func NewRootCmd() (*cobra.Command, app.EncodingConfig) { return rootCmd, encodingConfig } -// initAppConfig helps to override default appConfig template and configs. -// return "", nil if no custom configuration is required for the application. -func initAppConfig() (string, interface{}) { - // The following code snippet is just for reference. - - type CustomAppConfig struct { - serverconfig.Config - } - - // Optionally allow the chain developer to overwrite the SDK's default - // server config. - srvCfg := serverconfig.DefaultConfig() - // The SDK's default minimum gas price is set to "" (empty value) inside - // app.toml. If left empty by validators, the node will halt on startup. - // However, the chain developer can set a default app.toml value for their - // validators here. - // - // In summary: - // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their - // own app.toml config, - // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their - // own app.toml to override, or use this default value. - srvCfg.MinGasPrices = "0unibi" - - customAppConfig := CustomAppConfig{ - Config: *srvCfg, - } - - customAppTemplate := serverconfig.DefaultConfigTemplate - - return customAppTemplate, customAppConfig -} - /* 'initRootCmd' adds keybase, auxiliary RPC, query, and transaction (tx) child commands, then builds the rosetta root command given a protocol buffers @@ -147,7 +118,12 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig app.EncodingConfig) { pruning.PruningCmd(a.newApp), ) - server.AddCommands(rootCmd, app.DefaultNodeHome, a.newApp, a.appExport, addModuleInitFlags) + server.AddCommands( + rootCmd, + server.NewDefaultStartOptions(a.newApp, app.DefaultNodeHome), + a.appExport, + addModuleInitFlags, + ) // add keybase, auxiliary RPC, query, and tx child commands rootCmd.AddCommand( @@ -160,6 +136,9 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig app.EncodingConfig) { queryCommand(), txCommand(), keys.Commands(app.DefaultNodeHome), + + // EVM Tx Indexer force catch up command + server.NewEVMTxIndexCmd(), ) // TODO add rosettaj @@ -241,7 +220,7 @@ type appCreator struct { // newApp is an appCreator func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { - baseappOptions := server.DefaultBaseappOptions(appOpts) + baseappOptions := sdkserver.DefaultBaseappOptions(appOpts) return app.NewNibiruApp( logger, db, traceStore, true, diff --git a/cmd/nibid/cmd/root_test.go b/cmd/nibid/cmd/root_test.go index 8cff73aa8..cd4ef7be7 100644 --- a/cmd/nibid/cmd/root_test.go +++ b/cmd/nibid/cmd/root_test.go @@ -4,8 +4,8 @@ import ( "testing" // Nibiru - "github.com/NibiruChain/nibiru/app" - nibid "github.com/NibiruChain/nibiru/cmd/nibid/cmd" + "github.com/NibiruChain/nibiru/v2/app" + nibid "github.com/NibiruChain/nibiru/v2/cmd/nibid/cmd" // Cosmos-SDK svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" diff --git a/cmd/nibid/cmd/testnet.go b/cmd/nibid/cmd/testnet.go index 763ab7cf1..cb998e0f5 100644 --- a/cmd/nibid/cmd/testnet.go +++ b/cmd/nibid/cmd/testnet.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" + "cosmossdk.io/math" tmconfig "github.com/cometbft/cometbft/config" tmos "github.com/cometbft/cometbft/libs/os" tmrand "github.com/cometbft/cometbft/libs/rand" @@ -33,7 +34,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" ) var ( @@ -45,7 +46,9 @@ var ( ) // get cmd to initialize all files for tendermint testnet and application -func testnetCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator) *cobra.Command { +func testnetCmd( + mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, +) *cobra.Command { cmd := &cobra.Command{ Use: "testnet", Short: "Initialize files for a simapp testnet", @@ -213,8 +216,8 @@ func InitTestnet( valPubKeys[i], sdk.NewCoin(denoms.NIBI, valTokens), stakingtypes.NewDescription(nodeDirName, "", "", "", ""), - stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), - sdk.OneInt(), + stakingtypes.NewCommissionRates(math.LegacyOneDec(), math.LegacyOneDec(), math.LegacyOneDec()), + math.OneInt(), ) if err != nil { return err diff --git a/cmd/nibid/cmd/testnet_test.go b/cmd/nibid/cmd/testnet_test.go index 2054580f9..6c141124f 100644 --- a/cmd/nibid/cmd/testnet_test.go +++ b/cmd/nibid/cmd/testnet_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/v2/app" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/client" @@ -25,12 +25,12 @@ func Test_TestnetCmd(t *testing.T) { cfg, err := genutiltest.CreateDefaultTendermintConfig(home) require.NoError(t, err) - err = genutiltest.ExecInitCmd(app.ModuleBasics, home, encodingConfig.Marshaler) + err = genutiltest.ExecInitCmd(app.ModuleBasics, home, encodingConfig.Codec) require.NoError(t, err) serverCtx := server.NewContext(viper.New(), cfg, logger) clientCtx := client.Context{}. - WithCodec(encodingConfig.Marshaler). + WithCodec(encodingConfig.Codec). WithHomeDir(home). WithTxConfig(encodingConfig.TxConfig) @@ -49,6 +49,6 @@ func Test_TestnetCmd(t *testing.T) { appState, _, err := genutiltypes.GenesisStateFromGenFile(genFile) require.NoError(t, err) - bankGenState := banktypes.GetGenesisStateFromAppState(encodingConfig.Marshaler, appState) + bankGenState := banktypes.GetGenesisStateFromAppState(encodingConfig.Codec, appState) require.NotEmpty(t, bankGenState.Supply.String()) } diff --git a/cmd/nibid/main.go b/cmd/nibid/main.go index b34315c2f..381e603f1 100644 --- a/cmd/nibid/main.go +++ b/cmd/nibid/main.go @@ -6,8 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/cmd/nibid/cmd" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/cmd/nibid/cmd" ) func main() { diff --git a/config.yml b/config.yml new file mode 120000 index 000000000..c4efda42c --- /dev/null +++ b/config.yml @@ -0,0 +1 @@ +tools/ignite-config.yml \ No newline at end of file diff --git a/contrib/bashlib.sh b/contrib/bashlib.sh new file mode 100644 index 000000000..98c68d899 --- /dev/null +++ b/contrib/bashlib.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# ————————————————————————————————————————————————— +# COLORS: Terminal colors are set with ANSI escape codes. + +export COLOR_GREEN="\033[32m" +export COLOR_CYAN="\033[36m" +export COLOR_RESET="\033[0m" + +export COLOR_BLACK="\033[30m" +export COLOR_RED="\033[31m" +export COLOR_YELLOW="\033[33m" +export COLOR_BLUE="\033[34m" +export COLOR_MAGENTA="\033[35m" +export COLOR_WHITE="\033[37m" + +# Bright color definitions +export COLOR_BRIGHT_BLACK="\033[90m" +export COLOR_BRIGHT_RED="\033[91m" +export COLOR_BRIGHT_GREEN="\033[92m" +export COLOR_BRIGHT_YELLOW="\033[93m" +export COLOR_BRIGHT_BLUE="\033[94m" +export COLOR_BRIGHT_MAGENTA="\033[95m" +export COLOR_BRIGHT_CYAN="\033[96m" +export COLOR_BRIGHT_WHITE="\033[97m" + +# ————————————————————————————————————————————————— +# LOGGING + +# log_debug: Simple wrapper for `echo` with a DEBUG prefix. +log_debug() { + echo -e "${COLOR_CYAN}DEBUG${COLOR_RESET}" "$@" +} + +# log_error: ERROR messages in red, output to stderr. +log_error() { + echo -e "❌ ${COLOR_RED}ERROR:${COLOR_RESET}" "$@" >&2 +} + +log_success() { + echo -e "${COLOR_GREEN}✅ SUCCESS:${COLOR_RESET}" "$@" +} + +# log_warning: WARNING messages represent non-critical issues that might not +# require immediate action but should be noted as points of concern or failure. +log_warning() { + echo -e "${COLOR_YELLOW}WARNING${COLOR_RESET}" "$@" >&2 +} + +log_info() { + echo -e "${COLOR_MAGENTA}INFO${COLOR_RESET}" "$@" +} + +# ————————————————————————————————————————————————— +# OK Suffix: Functions used for error handling or validating inputs. + +# which_ok: Check if the given binary is in the $PATH. +# Returns code 0 on success and code 1 if the command fails. +which_ok() { + if which "$1" >/dev/null 2>&1; then + return 0 + else + log_error "$1 is not present in \$PATH" + return 1 + fi +} + +# source_ok (Function): Sources a bash script if it exists. +# Usage: source_ok [bash_script] +source_ok() { + local bash_script="$1" + if test -r "$bash_script"; then + # shellcheck disable=SC1090 + source "$bash_script" + fi +} + +env_var_ok() { + local env_var="$1" + if [[ -z "$env_var" ]]; then + log_error "expected env var to be set" + return 1 # Return 1 to indicate error (variable is not set) + else + return 0 # Return 0 to indicate success (variable is set) + fi +} diff --git a/contrib/docker-compose/README.md b/contrib/docker-compose/README.md index f588d5728..e888d4b90 100644 --- a/contrib/docker-compose/README.md +++ b/contrib/docker-compose/README.md @@ -1,13 +1,18 @@ # contrib/docker-compose -- [docker-compose-chaosnet](#docker-compose-chaosnet) - - [Usage Commands:](#usage-commands) - - [Nibiru node services](#nibiru-node-services) - - [Oracle feeder services](#oracle-feeder-services) - - [Hermes IBC relayer services](#hermes-ibc-relayer-services) - - [Faucet Service](#faucet-service) - - [Heart Monitor Services](#heart-monitor-services) -- [Reference Materials](#reference-materials) +- [contrib/docker-compose](#contribdocker-compose) + - [docker-compose-chaosnet](#docker-compose-chaosnet) + - [Usage](#usage) + - [Single validator node + pricefeeder](#single-validator-node--pricefeeder) + - [Two validator nodes + pricefeeder + IBC relayer](#two-validator-nodes--pricefeeder--ibc-relayer) + - [Single validator node + heartmonitor](#single-validator-node--heartmonitor) + - [Other Commands](#other-commands) + - [Services Overview](#services-overview) + - [Nibiru node services](#nibiru-node-services) + - [Pricefeeder services](#pricefeeder-services) + - [Hermes IBC relayer services](#hermes-ibc-relayer-services) + - [Heart Monitor Services](#heart-monitor-services) + - [Reference Materials](#reference-materials) ## docker-compose-chaosnet @@ -18,17 +23,38 @@ Nibiru-specific containers. Features: -- Data volume mounts ensure persistent storage. - Different ports are utilized to mimic a multi-chain configuration on a single machine. -- Enables testing of cross-chain transactions, chain health monitoring, liquidations, and more in a local Docker context across two chains. +- Enables testing of cross-chain transactions, chain health monitoring, and more in a local Docker context across two chains. -### Usage Commands: +## Usage + +### Single validator node + pricefeeder + +```sh +docker compose -f docker-compose-chaosnet.yml up +``` + +### Two validator nodes + pricefeeder + IBC relayer + +```sh +docker compose -f docker-compose-chaosnet.yml --profile ibc up +``` + +### Single validator node + heartmonitor + +```sh +docker compose -f docker-compose-chaosnet.yml --profile heartmonitor up +``` + +### Other Commands - `docker compose up`: Start the services. - `docker compose down`: Stop the services. - `docker compose restart`: Restart all services. - `docker compose ps`: List containers, their status, ports, etc. -- `docker compose logs`: View std output from containers +- `docker compose logs`: View std output from containers + +## Services Overview ### Nibiru node services @@ -36,7 +62,7 @@ Features: running on different ports, using unique mnemonics and chain IDs, imitating two independent blockchain networks. -### Oracle feeder services +### Pricefeeder services - `pricefeeder-0` and `pricefeeder-1` (Service): Two price feeder services that push price data to the respective Nibiru nodes. @@ -44,6 +70,7 @@ Features: ### Hermes IBC relayer services An IBC relayer is set up to connect the two chains using [hermes](https://hermes.informal.systems/). + 1. `hermes-keys-task-0` and `hermes-keys-task-1` (Service): Tasks to generate keys for the validators on `nibiru-0` and `nibiru-1`. 2. `hermes-client-connection-channel-task` (Service): Creates a new channel @@ -59,34 +86,24 @@ Brief IBC reference: Put simply, **connections** represent a secure communication line between two blockchain to transfer IBC **packets** (data). Once a connection is established, light client of two chains, usually called the source chain and destination -chain, is established. +chain, is established. Once a connection is established, **channels** can be formed. A channel represents a logical pathway for specific types communication over the connection (like token transfers and other relaying of IBC packets. -### Faucet Service - -Dispenses testnet tokens. Faucets provide liquidity on test chains. - -Repository: [NibiruChain/go-faucet](https://github.com/NibiruChain/go-faucet) - ### Heart Monitor Services - `heartmonitor-db`: A postgres database for the heart monitor. - `heartmonitor`: An indexing solution that populates a DB based on events and - block responses emitted from Nibiru nodes. - -- `liquidator` : Liquidates underwater positions using the tracked state inside - `hearmonitor-db`. + block responses emitted from Nibiru nodes. - `graphql`: GraphQL API for the heart monitor data. Used in the Nibiru web app and other off-chain tools. Repository: [NibiruChain/go-heartmonitor](https://github.com/NibiruChain/go-heartmonitor). - ## Reference Materials - [Docker Compose file](https://docs.docker.com/compose/compose-file/03-compose-file/) diff --git a/contrib/docker-compose/docker-compose-chaosnet.yml b/contrib/docker-compose/docker-compose-chaosnet.yml index 5548b2364..df9514b01 100644 --- a/contrib/docker-compose/docker-compose-chaosnet.yml +++ b/contrib/docker-compose/docker-compose-chaosnet.yml @@ -48,6 +48,7 @@ services: RPC_PORT: 36657 GRPC_PORT: 19090 LCD_PORT: 11317 + profiles: [ibc] healthcheck: test: ["CMD", "curl", "-f", "http://localhost:36657"] interval: 60s @@ -63,6 +64,7 @@ services: pricefeeder-1: image: ghcr.io/nibiruchain/pricefeeder:latest + profiles: [ibc] restart: always environment: CHAIN_ID: nibiru-localnet-1 @@ -77,6 +79,7 @@ services: hermes-keys-task-0: image: informalsystems/hermes:1.6.0 + profiles: [ibc] user: root command: [ @@ -107,6 +110,7 @@ services: hermes-keys-task-1: image: informalsystems/hermes:1.6.0 + profiles: [ibc] user: root command: [ @@ -139,6 +143,7 @@ services: hermes-client-connection-channel-task: image: informalsystems/hermes:1.6.0 + profiles: [ibc] user: root command: [ @@ -172,6 +177,7 @@ services: hermes: image: informalsystems/hermes:1.6.0 + profiles: [ibc] restart: always user: root command: ["start"] @@ -191,22 +197,9 @@ services: hermes-client-connection-channel-task: condition: service_completed_successfully - faucet: - restart: always - image: ghcr.io/nibiruchain/go-faucet:latest - environment: - NODE: nibiru-0:9090 - MNEMONIC: undo donkey arena rule old portion long forget rescue post stuff normal reduce raw unable warrior method stairs valley enhance glory lens sign zero - SEND_COINS: 11000000unibi,100000000unusd,100000000uusdt - MAX_SEND_COINS: 110000000unibi,1000000000unusd,1000000000uusdt - depends_on: - nibiru-0: - condition: service_healthy - ports: - - 8000:8000 - heartmonitor-db: image: postgres:14 + profiles: [heartmonitor] restart: always environment: POSTGRES_PASSWORD: postgres @@ -216,6 +209,8 @@ services: heartmonitor: image: ghcr.io/nibiruchain/go-heartmonitor:latest + platform: linux/amd64 + profiles: [heartmonitor] restart: always command: --clean volumes: @@ -231,26 +226,10 @@ services: heartmonitor-db: condition: service_started - liquidator: - image: ghcr.io/nibiruchain/go-heartmonitor:latest - restart: always - command: --liquidator - environment: - - DATABASE_URI=postgresql://postgres:postgres@heartmonitor-db:5432/heart-monitor?sslmode=disable - - TENDERMINT_RPC_ENDPOINT=http://nibiru-0:26657 - - GRPC_ENDPOINT=tcp://nibiru-0:9090 - - GRPC_INSECURE=true - - NO_PARTITIONS=true - - LIQUIDATOR_MNEMONIC=record damage person caution truly riot resource luxury rude guide mushroom athlete fantasy dentist friend mule depth salmon photo unfold exclude coyote idea evoke - - LIQUIDATOR_GAS_LIMIT_INITIAL=500000 - - LIQUIDATOR_GAS_MULTIPLIER=5 - - LIQUIDATOR_GAS_MAX_ATTEMPTS=10 - depends_on: - heartmonitor: - condition: service_started - graphql: image: ghcr.io/nibiruchain/go-heartmonitor:latest + platform: linux/amd64 + profiles: [heartmonitor] restart: always command: --graphql environment: diff --git a/contrib/docker-compose/docker-compose-multi-node.yml b/contrib/docker-compose/docker-compose-multi-node.yml deleted file mode 100644 index e5e5b986e..000000000 --- a/contrib/docker-compose/docker-compose-multi-node.yml +++ /dev/null @@ -1,64 +0,0 @@ -version: '3' - -services: - node0: - container_name: node0 - image: "nibiru/node" - ports: - - "26656-26657:26656-26657" - - "9090:9090" - - "1317:1317" - environment: - - ID=0 - volumes: - - ../../data/node0/nibid:/root/.nibid:Z - networks: - localnet: - ipv4_address: 192.168.11.2 - - node1: - container_name: node1 - image: "nibiru/node" - ports: - - "26659-26660:26656-26657" - environment: - - ID=1 - volumes: - - ../../data/node1/nibid:/root/.nibid:Z - networks: - localnet: - ipv4_address: 192.168.11.3 - - node2: - container_name: node2 - image: "nibiru/node" - environment: - - ID=2 - ports: - - "26661-26662:26656-26657" - volumes: - - ../../data/node2/nibid:/root/.nibid:Z - networks: - localnet: - ipv4_address: 192.168.11.4 - - node3: - container_name: node3 - image: "nibiru/node" - environment: - - ID=3 - ports: - - "26663-26664:26656-26657" - volumes: - - ../../data/node3/nibid:/root/.nibid:Z - networks: - localnet: - ipv4_address: 192.168.11.5 - -networks: - localnet: - driver: bridge - ipam: - driver: default - config: - - subnet: 192.168.11.0/16 diff --git a/contrib/docker/chaosnet.Dockerfile b/contrib/docker/chaosnet.Dockerfile index e14fff196..db9cea315 100644 --- a/contrib/docker/chaosnet.Dockerfile +++ b/contrib/docker/chaosnet.Dockerfile @@ -1,14 +1,15 @@ -FROM golang:1.19 AS builder +FROM golang:1.21 AS builder WORKDIR /nibiru -COPY go.sum go.mod ./ +# copy go.mod, go.sum to WORKDIR +COPY go.sum go.mod ./ RUN go mod download -COPY . . +# copy the rest of the project to WORKDIR +COPY . . RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg \ - --mount=type=cache,target=/nibiru/temp \ make build FROM alpine:latest @@ -17,6 +18,7 @@ WORKDIR /root RUN apk --no-cache add \ ca-certificates \ build-base \ + bash \ curl \ jq diff --git a/contrib/make/chaosnet.mk b/contrib/make/chaosnet.mk index df46d516b..6bcc88ef4 100644 --- a/contrib/make/chaosnet.mk +++ b/contrib/make/chaosnet.mk @@ -12,10 +12,22 @@ chaosnet-build: chaosnet: chaosnet-down docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml up --detach --build -# Stop chaosnet +# Run a chaosnet testnet locally with IBC enabled +.PHONY: chaosnet-ibc +chaosnet-ibc: chaosnet-down + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml --profile ibc up --detach --build + +# Run a chaosnet testnet locally with heartmonitor+graphql stack enabled +.PHONY: chaosnet-heartmonitor +chaosnet-heartmonitor: chaosnet-down + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml --profile heartmonitor up --detach --build + +# Stop chaosnet, need to specify all profiles to ensure all services are stopped +# See https://stackoverflow.com/questions/76781634/docker-compose-down-all-profiles .PHONY: chaosnet-down chaosnet-down: - docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml down --timeout 1 --volumes + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml --profile ibc down --timeout 1 --volumes + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml --profile heartmonitor down --timeout 1 --volumes ############################################################################### ### Chaosnet Logs ### @@ -25,10 +37,6 @@ chaosnet-down: chaosnet-logs: docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml logs -.PHONY: chaosnet-logs-faucet -chaosnet-logs-faucet: - docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml logs go-faucet - .PHONY: chaosnet-logs-pricefeeder chaosnet-logs-pricefeeder: docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml logs pricefeeder --follow @@ -41,9 +49,13 @@ chaosnet-logs-go-heartmonitor: ### Chaosnet SSH ### ############################################################################### -.PHONY: chaosnet-ssh-nibiru -chaosnet-ssh-nibiru: - docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml exec -it nibiru /bin/sh +.PHONY: chaosnet-ssh-nibiru-0 +chaosnet-ssh-nibiru-0: + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml exec -it nibiru-0 /bin/sh + +.PHONY: chaosnet-ssh-nibiru-1 +chaosnet-ssh-nibiru-1: + docker compose -f ./contrib/docker-compose/docker-compose-chaosnet.yml exec -it nibiru-1 /bin/sh .PHONY: chaosnet-ssh-go-heartmonitor chaosnet-ssh-go-heartmonitor: diff --git a/contrib/make/proto.mk b/contrib/make/proto.mk index 551e6cc0d..fc7dd54e7 100644 --- a/contrib/make/proto.mk +++ b/contrib/make/proto.mk @@ -3,7 +3,7 @@ ############################################################################### PROJECT_NAME = $(shell git remote get-url origin | xargs basename -s .git) -protoVer=0.13.5 +protoVer=0.14.0 protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer) containerProtoGen=$(PROJECT_NAME)-proto-gen-$(protoVer) containerProtoFmt=$(PROJECT_NAME)-proto-fmt-$(protoVer) @@ -25,4 +25,4 @@ proto-format: find ./ -not -path "./third_party/*" -name "*.proto" -exec clang-format -i {} \; ; fi proto-lint: - @$(DOCKER_BUF) lint --error-format=json \ No newline at end of file + @$(DOCKER_BUF) lint --error-format=json diff --git a/contrib/make/simulation.mk b/contrib/make/simulation.mk index 31836c6bf..cd9aa822b 100644 --- a/contrib/make/simulation.mk +++ b/contrib/make/simulation.mk @@ -1,43 +1,55 @@ -BINDIR = $(GOPATH)/bin -RUNSIM = $(BINDIR)/runsim SIMAPP = ./simapp -.PHONY: runsim -runsim: $(RUNSIM) -$(RUNSIM): - @echo "Installing runsim..." - @(cd /tmp && go install github.com/cosmos/tools/cmd/runsim@v1.0.0) - .PHONY: test-sim-nondeterminism test-sim-nondeterminism: @echo "Running non-determinism test..." - @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ - -NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h + @go test -mod=readonly -v $(SIMAPP) \ + -run TestAppStateDeterminism \ + -Enabled=true \ + -Params=params.json \ + -NumBlocks=100 \ + -BlockSize=200 \ + -Commit=true \ + -Period=0 \ + -Verbose=true \ + -timeout 30m .PHONY: test-sim-default-genesis-fast test-sim-default-genesis-fast: @echo "Running default genesis simulation..." - @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation \ - -Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v - -.PHONY: test-sim-custom-genesis-multi-seed -test-sim-custom-genesis-multi-seed: runsim - @echo "Running multi-seed custom genesis simulation..." - @$(RUNSIM) -SimAppPkg=$(SIMAPP) -ExitOnFail 400 5 TestFullAppSimulation - -.PHONY: test-sim-multi-seed-long -test-sim-multi-seed-long: runsim - @echo "Running long multi-seed application simulation. This may take awhile!" - @$(RUNSIM) -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 500 50 TestFullAppSimulation + @go test -mod=readonly -v $(SIMAPP) \ + -run TestFullAppSimulation \ + -Params=params.json \ + -Enabled=true \ + -NumBlocks=100 \ + -BlockSize=200 \ + -Commit=true \ + -Seed=99 \ + -Period=0 \ + -timeout 30m -.PHONY: test-sim-multi-seed-short -test-sim-multi-seed-short: runsim - @echo "Running short multi-seed application simulation. This may take awhile!" - @$(RUNSIM) -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 10 TestFullAppSimulation +.PHONY: test-sim-import-export +test-sim-import-export: + @echo "Running application import/export simulation. This may take several minutes..." + @go test -mod=readonly -v $(SIMAPP) \ + -run TestAppImportExport \ + -Params=params.json \ + -Enabled=true \ + -NumBlocks=100 \ + -Commit=true \ + -Seed=99 \ + -Period=5 \ + -timeout 30m -.PHONY: test-sim-benchmark-invariants -test-sim-benchmark-invariants: - @echo "Running simulation invariant benchmarks..." - @go test -mod=readonly $(SIMAPP) -benchmem -bench=BenchmarkInvariants -run=^$ \ - -Enabled=true -NumBlocks=1000 -BlockSize=200 \ - -Period=1 -Commit=true -Seed=57 -v -timeout 24h \ No newline at end of file +.PHONY: test-sim-after-import +test-sim-after-import: + @echo "Running application simulation-after-import. This may take several minutes..." + @go test -mod=readonly -v $(SIMAPP) \ + -run TestAppSimulationAfterImport \ + -Params=params.json \ + -Enabled=true \ + -NumBlocks=50 \ + -Commit=true \ + -Seed=99 \ + -Period=5 \ + -timeout 30m diff --git a/contrib/make/test.mk b/contrib/make/test.mk index 0b64c37c6..529220445 100644 --- a/contrib/make/test.mk +++ b/contrib/make/test.mk @@ -2,11 +2,14 @@ # Tests ######################################################################### -PACKAGES_NOSIMULATION = ${shell go list ./... | grep -v simapp} +.PHONY: test-unit +test-unit: + go test ./... -short -.PHONY: test-coverage -test-coverage: - go test ./... $(PACKAGES_NOSIMULATION) -short \ +.PHONY: test-coverage-unit +test-coverage-unit: + go test ./... -short \ + -tags=pebbledb \ -coverprofile=coverage.txt \ -covermode=atomic \ -race @@ -16,6 +19,7 @@ test-coverage: .PHONY: test-coverage-integration test-coverage-integration: go test ./... \ + -tags=pebbledb \ -coverprofile=coverage.txt \ -covermode=atomic \ -race diff --git a/contrib/scripts/chaosnet.sh b/contrib/scripts/chaosnet.sh index 42b60c613..ffee52fd8 100755 --- a/contrib/scripts/chaosnet.sh +++ b/contrib/scripts/chaosnet.sh @@ -1,4 +1,8 @@ -#!/bin/sh +#!/usr/bin/env bash +# +# This script is used in tandem with `contrib/docker/chaosnet.Dockerfile` to +# run nodes for Nibiru Chain networks inside docker containers. +# - See CHAOSNET.md for usage instructions. set -e # Set localnet settings @@ -8,7 +12,7 @@ LCD_PORT=${LCD_PORT:-"1317"} GRPC_PORT=${GRPC_PORT:-"9090"} RPC_PORT=${RPC_PORT:-"26657"} -rm -rf $HOME/.nibid +rm -rf "$HOME/.nibid" nibid init $CHAIN_ID --chain-id $CHAIN_ID --home $HOME/.nibid --overwrite nibid config keyring-backend test nibid config chain-id $CHAIN_ID @@ -21,16 +25,9 @@ sed -i 's/log_format = .*/log_format = "json"/' $HOME/.nibid/config/config.toml sed -i '/\[api\]/,+3 s/enable = false/enable = true/' $HOME/.nibid/config/app.toml sed -i 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/' $HOME/.nibid/config/app.toml sed -i "s/localhost:1317/0.0.0.0:$LCD_PORT/" $HOME/.nibid/config/app.toml +sed -i '/\[grpc\]/,+3 s/enable = false/enable = true/' $HOME/.nibid/config/app.toml sed -i "s/localhost:9090/0.0.0.0:$GRPC_PORT/" $HOME/.nibid/config/app.toml -echo "$MNEMONIC" | nibid keys add validator --recover -nibid genesis add-genesis-account $(nibid keys show validator -a) "10000000000000unibi,10000000000000unusd,10000000000000uusdt,10000000000000uusdc" -nibid genesis add-genesis-account nibi1wx9360p9rvy9m5cdhsua6qpdf9ktvwhjqw949s "10000000000000unibi,10000000000000unusd,10000000000000uusdt,10000000000000uusdc" # faucet -nibid genesis add-genesis-account nibi1g7vzqfthhf4l4vs6skyjj27vqhe97m5gp33hxy "10000000000000unibi" # liquidator -nibid genesis add-genesis-account nibi19n0clnacpjv0d3t8evvzp3fptlup9srjdqunzs "10000000000000unibi" # pricefeeder -nibid genesis gentx validator 900000000unibi --chain-id $CHAIN_ID -nibid genesis collect-gentxs - # ------------------------------------------------------------------------ # Configure genesis params # ------------------------------------------------------------------------ @@ -47,13 +44,28 @@ add_genesis_param() { mv $HOME/.nibid/config/tmp_genesis.json $HOME/.nibid/config/genesis.json } -curr_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -source "$curr_dir/feat-perp.sh" -add_genesis_perp_markets_offline +curr_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" + +# recover mnemonic +echo "$MNEMONIC" | nibid keys add validator --recover +nibid genesis add-genesis-account $(nibid keys show validator -a) "10000000000000unibi" + +# x/sudo +add_genesis_param ".app_state.sudo.sudoers.root = \"$(nibid keys show validator -a)\"" # x/oracle add_genesis_param '.app_state.oracle.params.twap_lookback_window = "900s"' add_genesis_param '.app_state.oracle.params.vote_period = "10"' add_genesis_param '.app_state.oracle.params.min_voters = "1"' +nibid genesis add-genesis-pricefeeder-delegation --validator $(nibid keys show validator -a --bech val) --pricefeeder nibi19n0clnacpjv0d3t8evvzp3fptlup9srjdqunzs + +# ------------------------------------------------------------------------ +# genesis accounts and balances +# ------------------------------------------------------------------------ +nibid genesis add-genesis-account nibi1wx9360p9rvy9m5cdhsua6qpdf9ktvwhjqw949s "10000000000000unibi" # faucet +nibid genesis add-genesis-account nibi1g7vzqfthhf4l4vs6skyjj27vqhe97m5gp33hxy "10000000000000unibi" # liquidator +nibid genesis add-genesis-account nibi19n0clnacpjv0d3t8evvzp3fptlup9srjdqunzs "10000000000000unibi" # pricefeeder -nibid genesis add-genesis-pricefeeder-delegation --validator $(nibid keys show validator -a --bech val) --pricefeeder nibi19n0clnacpjv0d3t8evvzp3fptlup9srjdqunzs \ No newline at end of file +# gen_txs +nibid genesis gentx validator 900000000unibi --chain-id $CHAIN_ID +nibid genesis collect-gentxs diff --git a/contrib/scripts/e2e/deploy-wasm.sh b/contrib/scripts/e2e/deploy-wasm.sh index 3e02119a7..aabe3659f 100644 --- a/contrib/scripts/e2e/deploy-wasm.sh +++ b/contrib/scripts/e2e/deploy-wasm.sh @@ -4,19 +4,22 @@ set -e BINARY="nibid" DENOM="unibi" CHAIN_ID="nibiru-localnet-0" -TXFLAG="--gas-prices 0.1$DENOM --gas auto --gas-adjustment 1.3 -y -b block --chain-id $CHAIN_ID" +TXFLAG="--gas-prices 0.1$DENOM --gas auto --gas-adjustment 1.3 -y -b async --chain-id $CHAIN_ID" # validator addr VALIDATOR_ADDR=$($BINARY keys show validator --address) echo "Validator address:" echo "$VALIDATOR_ADDR" -BALANCE_1=$($BINARY q bank balances $VALIDATOR_ADDR) +BALANCE_1=$($BINARY q bank balances "$VALIDATOR_ADDR") echo "Pre-store balance:" echo "$BALANCE_1" echo "TX Flags: $TXFLAG" -CONTRACT_CODE=$($BINARY tx wasm store "./contrib/scripts/e2e/contracts/cw_nameservice.wasm" --from validator $TXFLAG --output json | jq -r '.logs[0].events[-1].attributes[-1].value') +TX_HASH=$($BINARY tx wasm store "./contrib/scripts/e2e/contracts/cw_nameservice.wasm" --from validator $TXFLAG --output json | jq -r '.txhash' ) +sleep 3 + +CONTRACT_CODE=$($BINARY q tx $TX_HASH --output json | jq -r '.logs[0].events[-1].attributes[-1].value') echo "Stored: $CONTRACT_CODE" BALANCE_2=$($BINARY q bank balances $VALIDATOR_ADDR) @@ -25,6 +28,7 @@ echo "$BALANCE_2" INIT='{"purchase_price":{"amount":"100","denom":"unibi"},"transfer_price":{"amount":"999","denom":"unibi"}}' $BINARY tx wasm instantiate $CONTRACT_CODE "$INIT" --from validator $TXFLAG --label "awesome name service" --no-admin +sleep 3 CONTRACT_ADDRESS=$($BINARY query wasm list-contract-by-code $CONTRACT_CODE --output json | jq -r '.contracts[-1]') echo "Contract Address: $CONTRACT_ADDRESS" @@ -33,6 +37,7 @@ $BINARY query wasm contract $CONTRACT_ADDRESS # purchase a domain name $BINARY tx wasm execute $CONTRACT_ADDRESS '{"register":{"name":"uniques-domain"}}' --amount 100$DENOM --from validator $TXFLAG -y +sleep 3 # query registered name NAME_QUERY='{"resolve_record": {"name": "uniques-domain"}}' diff --git a/contrib/scripts/localnet.sh b/contrib/scripts/localnet.sh index b4e47c8f1..8266cf44e 100755 --- a/contrib/scripts/localnet.sh +++ b/contrib/scripts/localnet.sh @@ -54,15 +54,10 @@ echo_success() { echo_info "Parsing flags for the script..." -# $FLAG_NO_BUILD: toggles whether to build from source. The default -# behavior of the script is to run make install if the flag --no-build is not present. -FLAG_NO_BUILD=false +# $FLAG_SKIP_BUILD: toggles whether to build from source. The default +# behavior of the script is to run make install if the flag --no-build is omitted. +FLAG_SKIP_BUILD=false -# $FLAG_PERP: Feature flag for x/perp. Enabled with `--features perp`. -FLAG_PERP=false - -# $FLAG_SPOT: Feature flag for x/spot. Enabled with `--features spot`. -FLAG_SPOT=false build_from_source() { echo_info "Building from source..." @@ -77,39 +72,37 @@ build_from_source() { # enable_feature_flag: Enables feature flags variables if present enable_feature_flag() { case $1 in - perp) FLAG_PERP=true ;; - spot) FLAG_SPOT=true ;; - *) echo_error "Unknown feature: $1" ;; + spot) FLAG_SPOT=true ;; + *) echo_error "Unknown feature: $1" ;; esac } # Iterate over flags, handling the cases: "--no-build" and "--features" while [[ $# -gt 0 ]]; do case $1 in - --no-build) - FLAG_NO_BUILD=true - shift - ;; - --features) - shift # Remove '--features' from arguments - while [[ $# -gt 0 && $1 != --* ]]; do - enable_feature_flag "$1" - shift # Remove the feature name from arguments - done - ;; - *) shift ;; # Unknown arg + --no-build) + FLAG_SKIP_BUILD=true + shift + ;; + --features) + shift # Remove '--features' from arguments + while [[ $# -gt 0 && $1 != --* ]]; do + enable_feature_flag "$1" + shift # Remove the feature name from arguments + done + ;; + *) shift ;; # Unknown arg esac done -# Check if FLAG_NO_BUILD was set to true -if ! $FLAG_NO_BUILD; then + +# Check if FLAG_SKIP_BUILD was set to true +if ! $FLAG_SKIP_BUILD; then build_from_source fi echo_info "Features flags:" -echo "FLAG_NO_BUILD: $FLAG_NO_BUILD" -echo "FLAG_PERP: $FLAG_PERP" -echo "FLAG_SPOT: $FLAG_SPOT" +echo "FLAG_SKIP_BUILD: $FLAG_SKIP_BUILD" SEDOPTION="" if [[ "$OSTYPE" == "darwin"* ]]; then @@ -126,9 +119,11 @@ if pgrep -x "$BINARY" >/dev/null; then killall nibid fi -# Remove previous data +# Remove previous data, preserving keyring and config files echo_info "Removing previous chain data from $CHAIN_DIR..." -rm -rf "$CHAIN_DIR" +$BINARY tendermint unsafe-reset-all +rm -f "$CHAIN_DIR/config/genesis.json" +rm -rf "$CHAIN_DIR/config/gentx/" # Add directory for chain, exit if error if ! mkdir -p "$CHAIN_DIR" 2>/dev/null; then @@ -138,95 +133,66 @@ fi # Initialize nibid with "localnet" chain id echo_info "Initializing $CHAIN_ID..." -if $BINARY init nibiru-localnet-0 --chain-id $CHAIN_ID --overwrite; then +if $BINARY init $CHAIN_ID --chain-id $CHAIN_ID --overwrite; then echo_success "Successfully initialized $CHAIN_ID" else echo_error "Failed to initialize $CHAIN_ID" + exit 1 fi -# Configure keyring-backend to "test" -echo_info "Configuring keyring-backend..." -if $BINARY config keyring-backend test; then - echo_success "Successfully configured keyring-backend" -else - echo_error "Failed to configure keyring-backend" -fi +# nibid config +echo_info "Updating nibid config..." +$BINARY config keyring-backend test +$BINARY config chain-id $CHAIN_ID +$BINARY config broadcast-mode sync +$BINARY config output json +$BINARY config node "http://localhost:26657" +$BINARY config # Prints config. -# Configure chain-id -echo_info "Configuring chain-id..." -if $BINARY config chain-id $CHAIN_ID; then - echo_success "Successfully configured chain-id" -else - echo_error "Failed to configure chain-id" -fi +# Enable API Server +echo_info "config/app.toml: Enabling API server" +sed -i $SEDOPTION '/\[api\]/,+3 s/enable = false/enable = true/' $CHAIN_DIR/config/app.toml -# Configure broadcast mode -echo_info "Configuring broadcast mode..." -if $BINARY config broadcast-mode sync; then - echo_success "Successfully configured broadcast-mode" -else - echo_error "Failed to configure broadcast mode" -fi +# Enable GRPC Server +echo_info "config/app.toml: Enabling GRPC server" +sed -i $SEDOPTION '/\[grpc\]/,+3 s/enable = false/enable = true/' $CHAIN_DIR/config/app.toml -# Configure output mode -echo_info "Configuring output mode..." -if $BINARY config output json; then - echo_success "Successfully configured output mode" -else - echo_error "Failed to configure output mode" -fi -# Enable API Server -echo_info "Enabling API server" -if sed -i $SEDOPTION '/\[api\]/,+3 s/enable = false/enable = true/' $CHAIN_DIR/config/app.toml; then - echo_success "Successfully enabled API server" -else - echo_error "Failed to enable API server" -fi +# Enable JSON RPC Server +echo_info "config/app.toml: Enabling JSON API server" +sed -i $SEDOPTION '/\[json\-rpc\]/,+3 s/enable = false/enable = true/' $CHAIN_DIR/config/app.toml + +echo_info "config/app.toml: Enabling debug evm api" +sed -i $SEDOPTION '/\[json\-rpc\]/,+13 s/api = "eth,net,web3"/api = "eth,net,web3,debug"/' $CHAIN_DIR/config/app.toml + +echo_info "config/app.toml: Enabling evm indexer" +sed -i $SEDOPTION '/\[json\-rpc\]/,+51 s/enable-indexer = false/enable-indexer = true/' $CHAIN_DIR/config/app.toml # Enable Swagger Docs -echo_info "Enabling Swagger Docs" -if sed -i $SEDOPTION 's/swagger = false/swagger = true/' $CHAIN_DIR/config/app.toml; then - echo_success "Successfully enabled Swagger Docs" -else - echo_error "Failed to enable Swagger Docs" -fi +echo_info "config/app.toml: Enabling Swagger Docs" +sed -i $SEDOPTION 's/swagger = false/swagger = true/' $CHAIN_DIR/config/app.toml # Enable CORS for localnet -echo_info "Enabling CORS" -if sed -i $SEDOPTION 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/' $CHAIN_DIR/config/app.toml; then - echo_success "Successfully enabled CORS" -else - echo_error "Failed to enable CORS" -fi +echo_info "config/app.toml: Enabling CORS" +sed -i $SEDOPTION 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/' $CHAIN_DIR/config/app.toml echo_info "Adding genesis accounts..." val_key_name="validator" -echo "$MNEMONIC" | $BINARY keys add $val_key_name --recover -if $BINARY add-genesis-account $($BINARY keys show $val_key_name -a) $GENESIS_COINS; then - echo_success "Successfully added genesis account: $val_key_name" -else - echo_error "Failed to add genesis account: $val_key_name" +if ! $BINARY keys show $val_key_name; then + echo "$MNEMONIC" | $BINARY keys add $val_key_name --recover + echo_success "Successfully added key: $val_key_name" fi -val_address=$($BINARY keys list | jq -r '.[] | select(.name == "validator") | .address') +val_address=$($BINARY keys show $val_key_name -a) val_address=${val_address:-"nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl"} -echo_info "Adding gentx validator..." -if $BINARY genesis gentx $val_key_name 900000000unibi --chain-id $CHAIN_ID; then - echo_success "Successfully added gentx" -else - echo_error "Failed to add gentx" -fi - -echo_info "Collecting gentx..." -if $BINARY genesis collect-gentxs; then - echo_success "Successfully collected genesis txs into genesis.json" -else - echo_error "Failed to collect genesis txs" -fi +$BINARY add-genesis-account $val_address $GENESIS_COINS +# EVM encoded nibi address for the same account +$BINARY add-genesis-account nibi1cr6tg4cjvux00pj6zjqkh6d0jzg7mksaywxyl3 $GENESIS_COINS +$BINARY add-genesis-account nibi1ltez0kkshywzm675rkh8rj2eaf8et78cqjqrhc $GENESIS_COINS +echo_success "Successfully added genesis account: $val_key_name" # ------------------------------------------------------------------------ # Configure genesis params @@ -246,24 +212,6 @@ add_genesis_param() { echo_info "Configuring genesis params" -if $FLAG_PERP; then - curr_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" - source "$curr_dir/feat-perp.sh" - - if add_genesis_perp_markets_with_coingecko_prices; then - echo_success "set perp markets with coingecko prices" - elif add_genesis_perp_markets_offline; then - echo_success "set perp markets with offline defaults" - else - echo_error "failed to set genesis perp markets" - exit 1 - fi -fi - -# if $FLAG_SPOT; then -# # Perform any actions specific to the x/spot feature -# fi - # set validator as sudoer add_genesis_param '.app_state.sudo.sudoers.root = "'"$val_address"'"' @@ -272,9 +220,30 @@ price_btc="50000" price_eth="2000" add_genesis_param '.app_state.oracle.exchange_rates[0].pair = "ubtc:uuusd"' add_genesis_param '.app_state.oracle.exchange_rates[0].exchange_rate = "'"$price_btc"'"' -add_genesis_param '.app_state.oracle.exchange_rates[1].pair = "ueth:unusd"' +add_genesis_param '.app_state.oracle.exchange_rates[1].pair = "ueth:uuusd"' add_genesis_param '.app_state.oracle.exchange_rates[1].exchange_rate = "'"$price_eth"'"' +# ------------------------------------------------------------------------ +# Gentx +# ------------------------------------------------------------------------ + +echo_info "Adding gentx validator..." +if $BINARY genesis gentx $val_key_name 900000000unibi --chain-id $CHAIN_ID; then + echo_success "Successfully added gentx" +else + echo_error "Failed to add gentx" +fi + +echo_info "Collecting gentx..." +if $BINARY genesis collect-gentxs; then + echo_success "Successfully collected genesis txs into genesis.json" +else + echo_error "Failed to collect genesis txs" +fi + +# ------------------------------------------------------------------------ # Start the network +# ------------------------------------------------------------------------ + echo_info "Starting $CHAIN_ID in $CHAIN_DIR..." $BINARY start --home "$CHAIN_DIR" --pruning nothing diff --git a/contrib/scripts/protocgen.sh b/contrib/scripts/protocgen.sh index f3ffbfb8b..55c3c071e 100755 --- a/contrib/scripts/protocgen.sh +++ b/contrib/scripts/protocgen.sh @@ -16,5 +16,5 @@ done cd .. # move proto files to the right places -cp -r github.com/NibiruChain/nibiru/* ./ +cp -r github.com/NibiruChain/nibiru/v2/* ./ rm -rf github.com diff --git a/eth/account.pb.go b/eth/account.pb.go new file mode 100644 index 000000000..b081e47d5 --- /dev/null +++ b/eth/account.pb.go @@ -0,0 +1,378 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: eth/types/v1/account.proto + +package eth + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + types "github.com/cosmos/cosmos-sdk/x/auth/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// EthAccount implements the authtypes.AccountI interface and embeds an +// authtypes.BaseAccount type. It is compatible with the auth AccountKeeper. +type EthAccount struct { + // base_account is an authtypes.BaseAccount + *types.BaseAccount `protobuf:"bytes,1,opt,name=base_account,json=baseAccount,proto3,embedded=base_account" json:"base_account,omitempty" yaml:"base_account"` + // code_hash is the hash calculated from the code contents + CodeHash string `protobuf:"bytes,2,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty" yaml:"code_hash"` +} + +func (m *EthAccount) Reset() { *m = EthAccount{} } +func (m *EthAccount) String() string { return proto.CompactTextString(m) } +func (*EthAccount) ProtoMessage() {} +func (*EthAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_33844e397ad0a9a0, []int{0} +} +func (m *EthAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EthAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EthAccount.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EthAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_EthAccount.Merge(m, src) +} +func (m *EthAccount) XXX_Size() int { + return m.Size() +} +func (m *EthAccount) XXX_DiscardUnknown() { + xxx_messageInfo_EthAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_EthAccount proto.InternalMessageInfo + +func init() { + proto.RegisterType((*EthAccount)(nil), "eth.types.v1.EthAccount") +} + +func init() { proto.RegisterFile("eth/types/v1/account.proto", fileDescriptor_33844e397ad0a9a0) } + +var fileDescriptor_33844e397ad0a9a0 = []byte{ + // 321 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0x2d, 0xc9, 0xd0, + 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2f, 0x33, 0xd4, 0x4f, 0x4c, 0x4e, 0xce, 0x2f, 0xcd, 0x2b, + 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x49, 0x2d, 0xc9, 0xd0, 0x03, 0xcb, 0xe9, 0x95, + 0x19, 0x4a, 0xc9, 0x25, 0xe7, 0x17, 0xe7, 0xe6, 0x17, 0xeb, 0x27, 0x96, 0x96, 0x64, 0xe8, 0x97, + 0x19, 0x26, 0xa5, 0x96, 0x24, 0x1a, 0x82, 0x39, 0x10, 0xd5, 0x52, 0x92, 0x10, 0xf9, 0x78, 0x30, + 0x4f, 0x1f, 0xc2, 0x81, 0x4a, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x43, 0xc4, 0x41, 0x2c, 0x88, 0xa8, + 0xd2, 0x57, 0x46, 0x2e, 0x2e, 0xd7, 0x92, 0x0c, 0x47, 0x88, 0x9d, 0x42, 0x09, 0x5c, 0x3c, 0x49, + 0x89, 0xc5, 0xa9, 0xf1, 0x50, 0x37, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0x29, 0xe8, 0x41, + 0x4d, 0x02, 0xdb, 0x04, 0xb5, 0x56, 0xcf, 0x29, 0xb1, 0x38, 0x15, 0xaa, 0xcf, 0x49, 0xfa, 0xc2, + 0x3d, 0x79, 0xc6, 0x4f, 0xf7, 0xe4, 0x85, 0x2b, 0x13, 0x73, 0x73, 0xac, 0x94, 0x90, 0xcd, 0x50, + 0x0a, 0xe2, 0x4e, 0x42, 0xa8, 0x14, 0x32, 0xe4, 0xe2, 0x4c, 0xce, 0x4f, 0x49, 0x8d, 0xcf, 0x48, + 0x2c, 0xce, 0x90, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x74, 0x12, 0xf9, 0x74, 0x4f, 0x5e, 0x00, 0xa2, + 0x11, 0x2e, 0xa5, 0x14, 0xc4, 0x01, 0x62, 0x7b, 0x24, 0x16, 0x67, 0x58, 0x05, 0x75, 0x2c, 0x90, + 0x67, 0x78, 0xb1, 0x40, 0x9e, 0xe1, 0xd4, 0x16, 0x5d, 0xb7, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, + 0xbd, 0xe4, 0xfc, 0x5c, 0xa8, 0xf7, 0xa0, 0x94, 0x6e, 0x71, 0x4a, 0xb6, 0x7e, 0x05, 0x24, 0x60, + 0x20, 0xc1, 0x85, 0xcd, 0xcd, 0x50, 0x57, 0x78, 0x3a, 0xd9, 0x9d, 0x78, 0x24, 0xc7, 0x78, 0xe1, + 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, + 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x0a, 0x92, 0x0d, 0x7e, 0x99, 0x49, 0x99, 0x45, 0xa5, 0xce, 0x19, + 0x89, 0x99, 0x79, 0xfa, 0x79, 0x60, 0xb6, 0x7e, 0x99, 0x91, 0x7e, 0x6a, 0x49, 0x46, 0x12, 0x1b, + 0x38, 0xf8, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb0, 0x45, 0x85, 0x83, 0xbb, 0x01, 0x00, + 0x00, +} + +func (m *EthAccount) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EthAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EthAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CodeHash) > 0 { + i -= len(m.CodeHash) + copy(dAtA[i:], m.CodeHash) + i = encodeVarintAccount(dAtA, i, uint64(len(m.CodeHash))) + i-- + dAtA[i] = 0x12 + } + if m.BaseAccount != nil { + { + size, err := m.BaseAccount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintAccount(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintAccount(dAtA []byte, offset int, v uint64) int { + offset -= sovAccount(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *EthAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BaseAccount != nil { + l = m.BaseAccount.Size() + n += 1 + l + sovAccount(uint64(l)) + } + l = len(m.CodeHash) + if l > 0 { + n += 1 + l + sovAccount(uint64(l)) + } + return n +} + +func sovAccount(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAccount(x uint64) (n int) { + return sovAccount(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *EthAccount) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccount + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EthAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EthAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseAccount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccount + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAccount + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthAccount + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BaseAccount == nil { + m.BaseAccount = &types.BaseAccount{} + } + if err := m.BaseAccount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CodeHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAccount + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAccount + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAccount + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CodeHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAccount(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAccount + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAccount(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAccount + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAccount + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAccount + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAccount + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAccount + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAccount + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAccount = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAccount = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAccount = fmt.Errorf("proto: unexpected end of group") +) diff --git a/eth/assert.go b/eth/assert.go new file mode 100644 index 000000000..09e5bd328 --- /dev/null +++ b/eth/assert.go @@ -0,0 +1,43 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eth + +import ( + "bytes" + + errorsmod "cosmossdk.io/errors" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/common" +) + +// IsEmptyHash returns true if the hash corresponds to an empty ethereum hex hash. +func IsEmptyHash(hash string) bool { + return bytes.Equal(common.HexToHash(hash).Bytes(), common.Hash{}.Bytes()) +} + +// IsZeroAddress returns true if the address corresponds to an empty ethereum hex address. +func IsZeroAddress(address string) bool { + return bytes.Equal(common.HexToAddress(address).Bytes(), common.Address{}.Bytes()) +} + +// ValidateAddress returns an error if the provided string is either not a hex formatted string address +func ValidateAddress(address string) error { + if !common.IsHexAddress(address) { + return errorsmod.Wrapf( + errortypes.ErrInvalidAddress, "address '%s' is not a valid ethereum hex address", + address, + ) + } + return nil +} + +// ValidateNonZeroAddress returns an error if the provided string is not a hex +// formatted string address or is equal to zero +func ValidateNonZeroAddress(address string) error { + if IsZeroAddress(address) { + return errorsmod.Wrapf( + errortypes.ErrInvalidAddress, "address '%s' must not be zero", + address, + ) + } + return ValidateAddress(address) +} diff --git a/eth/assert_test.go b/eth/assert_test.go new file mode 100644 index 000000000..f6d38beb7 --- /dev/null +++ b/eth/assert_test.go @@ -0,0 +1,145 @@ +package eth_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func TestIsEmptyHash(t *testing.T) { + testCases := []struct { + name string + hash string + expEmpty bool + }{ + { + "empty string", "", true, + }, + { + "zero hash", common.Hash{}.String(), true, + }, + + { + "non-empty hash", common.BytesToHash([]byte{1, 2, 3, 4}).String(), false, + }, + } + + for _, tc := range testCases { + require.Equal(t, tc.expEmpty, eth.IsEmptyHash(tc.hash), tc.name) + } +} + +func TestIsZeroAddress(t *testing.T) { + testCases := []struct { + name string + address string + expEmpty bool + }{ + { + "empty string", "", true, + }, + { + "zero address", common.Address{}.String(), true, + }, + + { + "non-empty address", common.BytesToAddress([]byte{1, 2, 3, 4}).String(), false, + }, + } + + for _, tc := range testCases { + require.Equal(t, tc.expEmpty, eth.IsZeroAddress(tc.address), tc.name) + } +} + +func TestValidateAddress(t *testing.T) { + testCases := []struct { + name string + address string + expError bool + }{ + { + "empty string", "", true, + }, + { + "invalid address", "0x", true, + }, + { + "zero address", common.Address{}.String(), false, + }, + { + "valid address", evmtest.NewEthPrivAcc().EthAddr.Hex(), false, + }, + } + + for _, tc := range testCases { + err := eth.ValidateAddress(tc.address) + + if tc.expError { + require.Error(t, err, tc.name) + } else { + require.NoError(t, err, tc.name) + } + } +} + +func TestValidateNonZeroAddress(t *testing.T) { + testCases := []struct { + name string + address string + expError bool + }{ + { + "empty string", "", true, + }, + { + "invalid address", "0x", true, + }, + { + "zero address", common.Address{}.String(), true, + }, + { + "valid address", evmtest.NewEthPrivAcc().EthAddr.Hex(), false, + }, + } + + for _, tc := range testCases { + err := eth.ValidateNonZeroAddress(tc.address) + + if tc.expError { + require.Error(t, err, tc.name) + } else { + require.NoError(t, err, tc.name) + } + } +} + +func TestSafeInt64(t *testing.T) { + testCases := []struct { + name string + value uint64 + expError bool + }{ + { + "no overflow", 10, false, + }, + { + "overflow", 18446744073709551615, true, + }, + } + + for _, tc := range testCases { + value, err := eth.SafeInt64(tc.value) + if tc.expError { + require.Error(t, err, tc.name) + continue + } + + require.NoError(t, err, tc.name) + require.Equal(t, int64(tc.value), value, tc.name) + } +} diff --git a/eth/chain_id.go b/eth/chain_id.go new file mode 100644 index 000000000..541e8fa0b --- /dev/null +++ b/eth/chain_id.go @@ -0,0 +1,75 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eth + +import ( + "fmt" + "math/big" + "regexp" + "strings" + + "github.com/NibiruChain/nibiru/v2/app/appconst" +) + +var ( + // one of any lower case letter from "a"-"z" + regexChainID = `[a-z]{1,}` + // one of either "_" or "-" + regexEIP155Separator = `[_-]{1}` + // one of "_" + // regexEIP155Separator = `_{1}` + regexEIP155 = `[1-9][0-9]*` + regexEpochSeparator = `-{1}` + regexEpoch = `[1-9][0-9]*` + nibiruEvmChainId = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)%s(%s)$`, + regexChainID, + regexEIP155Separator, + regexEIP155, + regexEpochSeparator, + regexEpoch)) +) + +// IsValidChainID returns false if the given chain identifier is incorrectly +// formatted. +func IsValidChainID(chainID string) bool { + if len(chainID) > 48 { + return false + } + + return nibiruEvmChainId.MatchString(chainID) +} + +// ParseEthChainID parses a string chain identifier's +// to an Ethereum-compatible chain-id in *big.Int format. +// +// This function uses Nibiru's map of chain IDs defined in Nibiru/app/appconst +// rather than the regex of EIP155, which is implemented by +// ParseEthChainIDStrict. +func ParseEthChainID(chainID string) *big.Int { + return appconst.GetEthChainID(chainID) +} + +// ParseEthChainIDStrict parses a string chain identifier's epoch to an +// Ethereum-compatible chain-id in *big.Int format. The function returns an error +// if the chain-id has an invalid format +func ParseEthChainIDStrict(chainID string) (*big.Int, error) { + chainID = strings.TrimSpace(chainID) + if len(chainID) > 48 { + return nil, ErrInvalidChainID.Wrapf( + `chain-id input "%s" cannot exceed 48 chars`, chainID) + } + + matches := nibiruEvmChainId.FindStringSubmatch(chainID) + if matches == nil || len(matches) != 4 || matches[1] == "" { + return nil, ErrInvalidChainID.Wrapf( + `chain-id input "%s", matches "%v"`, chainID, matches) + } + + // verify that the chain-id entered is a base 10 integer + chainIDInt, ok := new(big.Int).SetString(matches[2], 10) + if !ok { + return nil, ErrInvalidChainID.Wrapf( + `epoch "%s" must be base-10 integer format`, matches[2]) + } + + return chainIDInt, nil +} diff --git a/eth/chain_id_test.go b/eth/chain_id_test.go new file mode 100644 index 000000000..2594c13c0 --- /dev/null +++ b/eth/chain_id_test.go @@ -0,0 +1,130 @@ +package eth + +import ( + "math/big" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestParseChainID_Happy(t *testing.T) { + testCases := []struct { + name string + chainID string + expInt *big.Int + }{ + { + chainID: "nibiru_1-1", + name: "valid chain-id, single digit", + expInt: big.NewInt(1), + }, + { + chainID: "cataclysm_256-1", + name: "valid chain-id, multiple digits", + expInt: big.NewInt(256), + }, + { + chainID: "cataclysm-4-20", + name: "valid chain-id, dashed, multiple digits", + expInt: big.NewInt(4), + }, + { + chainID: "chain-1-1", + name: "valid chain-id, double dash", + expInt: big.NewInt(1), + }, + } + + for _, tc := range testCases { + chainIDEpoch, err := ParseEthChainIDStrict(tc.chainID) + require.NoError(t, err, tc.name) + var errMsg string = "" + if err != nil { + errMsg = err.Error() + } + assert.NoError(t, err, tc.name, errMsg) + require.Equal(t, tc.expInt, chainIDEpoch, tc.name) + require.True(t, IsValidChainID(tc.chainID)) + } +} + +func TestParseChainID_Sad(t *testing.T) { + testCases := []struct { + name string + chainID string + }{ + { + chainID: "chain_1_1", + name: "invalid chain-id, double underscore", + }, + { + chainID: "-", + name: "invalid chain-id, dash only", + }, + { + chainID: "-1", + name: "invalid chain-id, undefined identifier and EIP155", + }, + { + chainID: "_1-1", + name: "invalid chain-id, undefined identifier", + }, + { + chainID: "NIBIRU_1-1", + name: "invalid chain-id, uppercases", + }, + { + chainID: "Nibiru_1-1", + name: "invalid chain-id, mixed cases", + }, + { + chainID: "$&*#!_1-1", + name: "invalid chain-id, special chars", + }, + { + chainID: "nibiru_001-1", + name: "invalid eip155 chain-id, cannot start with 0", + }, + { + chainID: "nibiru_0x212-1", + name: "invalid eip155 chain-id, cannot invalid base", + }, + { + chainID: "nibiru_1-0x212", + name: "invalid eip155 chain-id, cannot invalid base", + }, + { + chainID: "nibiru_nibiru_9000-1", + name: "invalid eip155 chain-id, non-integer", + }, + { + chainID: "nibiru_-", + name: "invalid epoch, undefined", + }, + { + chainID: " ", + name: "blank chain ID", + }, + { + chainID: "", + name: "empty chain ID", + }, + { + chainID: "_-", + name: "empty content for chain id, eip155 and epoch numbers", + }, + { + chainID: "nibiru_" + strings.Repeat("1", 45) + "-1", + name: "long chain-id", + }, + } + + for _, tc := range testCases { + chainIDEpoch, err := ParseEthChainIDStrict(tc.chainID) + require.Error(t, err, tc.name) + require.Nil(t, chainIDEpoch) + require.False(t, IsValidChainID(tc.chainID), tc.name) + } +} diff --git a/eth/codec.go b/eth/codec.go new file mode 100644 index 000000000..e8d006a93 --- /dev/null +++ b/eth/codec.go @@ -0,0 +1,50 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eth + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/NibiruChain/nibiru/v2/app/appconst" +) + +const ( + EthBaseDenom = appconst.BondDenom + // EIP155ChainID_Testnet: Chain ID for a testnet Nibiru following the + // format proposed by Vitalik in EIP155. + EIP155ChainID_Testnet = "nibirutest_420-1" + + DefaultGasPrice = 20 + + // ProtocolVersion: Latest supported version of the Ethereum protocol. + // Matches the message types and expected APIs. + // As of April, 2024, the highest protocol version on Ethereum mainnet is + // "eth/68". + // See https://github.com/ethereum/devp2p/blob/master/caps/eth.md#change-log + // for the historical summary of each version. + ProtocolVersion = 65 +) + +// RegisterInterfaces registers the tendermint concrete client-related +// implementations and interfaces. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + // proto name: "cosmos.auth.v1beta1.AccountI" + registry.RegisterImplementations( + (*authtypes.AccountI)(nil), + &EthAccount{}, + // Also impl by: [ + // &authtypes.BaseAccount{}, + // &authtypes.ModuleAccount{}, + // ] + ) + + // proto name: "cosmos.auth.v1beta1.GenesisAccount" + registry.RegisterImplementations( + (*authtypes.GenesisAccount)(nil), + &EthAccount{}, + // Also impl by: [ + // &authtypes.BaseAccount{}, + // &authtypes.ModuleAccount{}, + // ] + ) +} diff --git a/eth/codec_test.go b/eth/codec_test.go new file mode 100644 index 000000000..32c9d62c9 --- /dev/null +++ b/eth/codec_test.go @@ -0,0 +1,93 @@ +package eth + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/suite" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +type CodecTestSuite struct { + suite.Suite +} + +func TestCodecSuite(t *testing.T) { + suite.Run(t, new(CodecTestSuite)) +} + +func (suite *CodecTestSuite) TestRegisterInterfaces() { + type ProtoNameInfo struct { + ProtoName string + Interface interface{} + WantImpls []string + } + protoInfos := []ProtoNameInfo{ + { + ProtoName: "cosmos.auth.v1beta1.AccountI", + Interface: new(authtypes.AccountI), + WantImpls: []string{ + "/eth.types.v1.EthAccount", + "/cosmos.auth.v1beta1.BaseAccount", + "/cosmos.auth.v1beta1.ModuleAccount", + }, + }, + { + ProtoName: "cosmos.auth.v1beta1.GenesisAccount", + Interface: new(authtypes.GenesisAccount), + WantImpls: []string{ + "/eth.types.v1.EthAccount", + "/cosmos.auth.v1beta1.BaseAccount", + "/cosmos.auth.v1beta1.ModuleAccount", + }, + }, + } + + // ------------------------------------------- + // Case 1: Setup: Register all interfaces under test + // ------------------------------------------- + registry := codectypes.NewInterfaceRegistry() + for _, protoInfo := range protoInfos { + registry.RegisterInterface(protoInfo.ProtoName, protoInfo.Interface) + } + RegisterInterfaces(registry) + authtypes.RegisterInterfaces(registry) + sdktx.RegisterInterfaces(registry) + + // Test: Assert that all expected protobuf interface implementations are + // registered (base + Ethereum) + for _, protoInfo := range protoInfos { + gotImpls := registry.ListImplementations(protoInfo.ProtoName) + suite.Require().ElementsMatch(protoInfo.WantImpls, gotImpls) + } + + // ------------------------------------------- + // Case 2: Setup: Register only eth interfaces + // ------------------------------------------- + registry = codectypes.NewInterfaceRegistry() + for _, protoInfo := range protoInfos { + registry.RegisterInterface(protoInfo.ProtoName, protoInfo.Interface) + } + RegisterInterfaces(registry) + + // Test: Assert that all expected protobuf interface implementations are + // registered (Ethereum only) + for _, protoInfo := range protoInfos { + gotImpls := registry.ListImplementations(protoInfo.ProtoName) + wantImpls := filterImplsForEth(protoInfo.WantImpls) + suite.Require().ElementsMatch(wantImpls, gotImpls) + } +} + +func filterImplsForEth(implTypeUrls []string) []string { + typeUrls := []string{} + for _, typeUrl := range implTypeUrls { + if strings.Contains(typeUrl, "eth") { + typeUrls = append(typeUrls, typeUrl) + } + } + return typeUrls +} diff --git a/eth/crypto/codec/amino.go b/eth/crypto/codec/amino.go new file mode 100644 index 000000000..380b8e5cd --- /dev/null +++ b/eth/crypto/codec/amino.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package codec + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" +) + +// RegisterCrypto registers all crypto dependency types with the provided Amino +// codec. +func RegisterCrypto(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(ðsecp256k1.PubKey{}, + ethsecp256k1.PubKeyName, nil) + cdc.RegisterConcrete(ðsecp256k1.PrivKey{}, + ethsecp256k1.PrivKeyName, nil) + + keyring.RegisterLegacyAminoCodec(cdc) + cryptocodec.RegisterCrypto(cdc) + + // NOTE: update SDK's amino codec to include the ethsecp256k1 keys. + // DO NOT REMOVE unless deprecated on the SDK. + legacy.Cdc = cdc +} diff --git a/eth/crypto/codec/codec.go b/eth/crypto/codec/codec.go new file mode 100644 index 000000000..c888f7b2c --- /dev/null +++ b/eth/crypto/codec/codec.go @@ -0,0 +1,15 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package codec + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" +) + +// RegisterInterfaces register the cryptographic key concrete types. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations((*cryptotypes.PubKey)(nil), ðsecp256k1.PubKey{}) + registry.RegisterImplementations((*cryptotypes.PrivKey)(nil), ðsecp256k1.PrivKey{}) +} diff --git a/eth/crypto/ethsecp256k1/benchmark_test.go b/eth/crypto/ethsecp256k1/benchmark_test.go new file mode 100644 index 000000000..815cc5832 --- /dev/null +++ b/eth/crypto/ethsecp256k1/benchmark_test.go @@ -0,0 +1,34 @@ +package ethsecp256k1 + +import ( + "fmt" + "testing" +) + +func BenchmarkGenerateKey(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := GenerateKey(); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkPubKey_VerifySignature(b *testing.B) { + privKey, err := GenerateKey() + if err != nil { + b.Fatal(err) + } + pubKey := privKey.PubKey() + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + msg := []byte(fmt.Sprintf("%10d", i)) + sig, err := privKey.Sign(msg) + if err != nil { + b.Fatal(err) + } + pubKey.VerifySignature(msg, sig) + } +} diff --git a/eth/crypto/ethsecp256k1/ethsecp256k1.go b/eth/crypto/ethsecp256k1/ethsecp256k1.go new file mode 100644 index 000000000..7a341931e --- /dev/null +++ b/eth/crypto/ethsecp256k1/ethsecp256k1.go @@ -0,0 +1,249 @@ +// Copyright (c) 2023-2024 Nibi, Inc. + +package ethsecp256k1 + +import ( + "bytes" + "crypto/ecdsa" + "crypto/subtle" + "fmt" + + errorsmod "cosmossdk.io/errors" + tmcrypto "github.com/cometbft/cometbft/crypto" + "github.com/cosmos/cosmos-sdk/codec" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/eth/eip712" +) + +const ( + // PrivKeySize defines the size of the PrivKey bytes + PrivKeySize = 32 + // PubKeySize defines the size of the PubKey bytes + PubKeySize = 33 + // KeyType is the string constant for the Secp256k1 algorithm + KeyType = "eth_secp256k1" +) + +// Amino encoding names +const ( + // PrivKeyName defines the amino encoding name for the EthSecp256k1 private key + PrivKeyName = "ethermint/PrivKeyEthSecp256k1" + // PubKeyName defines the amino encoding name for the EthSecp256k1 public key + PubKeyName = "ethermint/PubKeyEthSecp256k1" +) + +// ---------------------------------------------------------------------------- +// secp256k1 Private Key + +var ( + _ cryptotypes.PrivKey = &PrivKey{} + _ codec.AminoMarshaler = &PrivKey{} +) + +// GenerateKey generates a new random private key. It returns an error upon +// failure. +func GenerateKey() (*PrivKey, error) { + priv, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + + return &PrivKey{ + Key: crypto.FromECDSA(priv), + }, nil +} + +// Bytes returns the byte representation of the ECDSA Private Key. +func (privKey PrivKey) Bytes() []byte { + bz := make([]byte, len(privKey.Key)) + copy(bz, privKey.Key) + + return bz +} + +// PubKey returns the ECDSA private key's public key. If the privkey is not valid +// it returns a nil value. +func (privKey PrivKey) PubKey() cryptotypes.PubKey { + ecdsaPrivKey, err := privKey.ToECDSA() + if err != nil { + return nil + } + + return &PubKey{ + Key: crypto.CompressPubkey(&ecdsaPrivKey.PublicKey), + } +} + +// Equals returns true if two ECDSA private keys are equal and false otherwise. +func (privKey PrivKey) Equals(other cryptotypes.LedgerPrivKey) bool { + return privKey.Type() == other.Type() && subtle.ConstantTimeCompare(privKey.Bytes(), other.Bytes()) == 1 +} + +// Type returns eth_secp256k1 +func (privKey PrivKey) Type() string { + return KeyType +} + +// MarshalAmino overrides Amino binary marshaling. +func (privKey PrivKey) MarshalAmino() ([]byte, error) { + return privKey.Key, nil +} + +// UnmarshalAmino overrides Amino binary marshaling. +func (privKey *PrivKey) UnmarshalAmino(bz []byte) error { + if len(bz) != PrivKeySize { + return fmt.Errorf("invalid privkey size, expected %d got %d", PrivKeySize, len(bz)) + } + privKey.Key = bz + + return nil +} + +// MarshalAminoJSON overrides Amino JSON marshaling. +func (privKey PrivKey) MarshalAminoJSON() ([]byte, error) { + // When we marshal to Amino JSON, we don't marshal the "key" field itself, + // just its contents (i.e. the key bytes). + return privKey.MarshalAmino() +} + +// UnmarshalAminoJSON overrides Amino JSON marshaling. +func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error { + return privKey.UnmarshalAmino(bz) +} + +// Sign creates a recoverable ECDSA signature on the secp256k1 curve over the +// provided hash of the message. The produced signature is 65 bytes +// where the last byte contains the recovery ID. +func (privKey PrivKey) Sign(digestBz []byte) ([]byte, error) { + // TODO: remove + if len(digestBz) != crypto.DigestLength { + digestBz = crypto.Keccak256Hash(digestBz).Bytes() + } + + key, err := privKey.ToECDSA() + if err != nil { + return nil, err + } + + return crypto.Sign(digestBz, key) +} + +// ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type. +func (privKey PrivKey) ToECDSA() (*ecdsa.PrivateKey, error) { + return crypto.ToECDSA(privKey.Bytes()) +} + +// ---------------------------------------------------------------------------- +// secp256k1 Public Key + +var ( + _ cryptotypes.PubKey = &PubKey{} + _ codec.AminoMarshaler = &PubKey{} +) + +// Address returns the address of the ECDSA public key. +// The function will return an empty address if the public key is invalid. +func (pubKey PubKey) Address() tmcrypto.Address { + pubk, err := crypto.DecompressPubkey(pubKey.Key) + if err != nil { + return nil + } + + return tmcrypto.Address(crypto.PubkeyToAddress(*pubk).Bytes()) +} + +// Bytes returns the raw bytes of the ECDSA public key. +func (pubKey PubKey) Bytes() []byte { + bz := make([]byte, len(pubKey.Key)) + copy(bz, pubKey.Key) + + return bz +} + +// String implements the fmt.Stringer interface. +func (pubKey PubKey) String() string { + return fmt.Sprintf("EthPubKeySecp256k1{%X}", pubKey.Key) +} + +// Type returns eth_secp256k1 +func (pubKey PubKey) Type() string { + return KeyType +} + +// Equals returns true if the pubkey type is the same and their bytes are deeply equal. +func (pubKey PubKey) Equals(other cryptotypes.PubKey) bool { + return pubKey.Type() == other.Type() && bytes.Equal(pubKey.Bytes(), other.Bytes()) +} + +// MarshalAmino overrides Amino binary marshaling. +func (pubKey PubKey) MarshalAmino() ([]byte, error) { + return pubKey.Key, nil +} + +// UnmarshalAmino overrides Amino binary marshaling. +func (pubKey *PubKey) UnmarshalAmino(bz []byte) error { + if len(bz) != PubKeySize { + return errorsmod.Wrapf(errortypes.ErrInvalidPubKey, "invalid pubkey size, expected %d, got %d", PubKeySize, len(bz)) + } + pubKey.Key = bz + + return nil +} + +// MarshalAminoJSON overrides Amino JSON marshaling. +func (pubKey PubKey) MarshalAminoJSON() ([]byte, error) { + // When we marshal to Amino JSON, we don't marshal the "key" field itself, + // just its contents (i.e. the key bytes). + return pubKey.MarshalAmino() +} + +// UnmarshalAminoJSON overrides Amino JSON marshaling. +func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error { + return pubKey.UnmarshalAmino(bz) +} + +// VerifySignature verifies that the ECDSA public key created a given signature over +// the provided message. It will calculate the Keccak256 hash of the message +// prior to verification and approve verification if the signature can be verified +// from either the original message or its EIP-712 representation. +// +// CONTRACT: The signature should be in [R || S] format. +func (pubKey PubKey) VerifySignature(msg, sig []byte) bool { + return pubKey.verifySignatureECDSA(msg, sig) || pubKey.verifySignatureAsEIP712(msg, sig) +} + +// Verifies the signature as an EIP-712 signature by first converting the message payload +// to EIP-712 object bytes, then performing ECDSA verification on the hash. This is to support +// signing a Cosmos payload using EIP-712. +func (pubKey PubKey) verifySignatureAsEIP712(msg, sig []byte) bool { + eip712Bytes, err := eip712.GetEIP712BytesForMsg(msg) + if err != nil { + return false + } + + if pubKey.verifySignatureECDSA(eip712Bytes, sig) { + return true + } + + // Try verifying the signature using the legacy EIP-712 encoding + legacyEIP712Bytes, err := eip712.LegacyGetEIP712BytesForMsg(msg) + if err != nil { + return false + } + + return pubKey.verifySignatureECDSA(legacyEIP712Bytes, sig) +} + +// Perform standard ECDSA signature verification for the given raw bytes and signature. +func (pubKey PubKey) verifySignatureECDSA(msg, sig []byte) bool { + if len(sig) == crypto.SignatureLength { + // remove recovery ID (V) if contained in the signature + sig = sig[:len(sig)-1] + } + + // the signature needs to be in [R || S] format when provided to VerifySignature + return crypto.VerifySignature(pubKey.Key, crypto.Keccak256Hash(msg).Bytes(), sig) +} diff --git a/eth/crypto/ethsecp256k1/ethsecp256k1_test.go b/eth/crypto/ethsecp256k1/ethsecp256k1_test.go new file mode 100644 index 000000000..044f1c20b --- /dev/null +++ b/eth/crypto/ethsecp256k1/ethsecp256k1_test.go @@ -0,0 +1,124 @@ +package ethsecp256k1 + +import ( + "encoding/base64" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/secp256k1" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +) + +func TestPrivKey(t *testing.T) { + // validate type and equality + privKey, err := GenerateKey() + require.NoError(t, err) + require.Implements(t, (*cryptotypes.PrivKey)(nil), privKey) + + // validate inequality + privKey2, err := GenerateKey() + require.NoError(t, err) + require.False(t, privKey.Equals(privKey2)) + + // validate Ethereum address equality + addr := privKey.PubKey().Address() + key, err := privKey.ToECDSA() + require.NoError(t, err) + expectedAddr := crypto.PubkeyToAddress(key.PublicKey) + require.Equal(t, expectedAddr.Bytes(), addr.Bytes()) + + // validate we can sign some bytes + msg := []byte("hello world") + sigHash := crypto.Keccak256Hash(msg) + expectedSig, err := secp256k1.Sign(sigHash.Bytes(), privKey.Bytes()) + require.NoError(t, err) + + sig, err := privKey.Sign(sigHash.Bytes()) + require.NoError(t, err) + require.Equal(t, expectedSig, sig) +} + +func TestPrivKey_PubKey(t *testing.T) { + privKey, err := GenerateKey() + require.NoError(t, err) + + // validate type and equality + pubKey := &PubKey{ + Key: privKey.PubKey().Bytes(), + } + require.Implements(t, (*cryptotypes.PubKey)(nil), pubKey) + + // validate inequality + privKey2, err := GenerateKey() + require.NoError(t, err) + require.False(t, pubKey.Equals(privKey2.PubKey())) + + // validate signature + msg := []byte("hello world") + sigHash := crypto.Keccak256Hash(msg) + sig, err := privKey.Sign(sigHash.Bytes()) + require.NoError(t, err) + + res := pubKey.VerifySignature(msg, sig) + require.True(t, res) +} + +func TestMarshalAmino(t *testing.T) { + aminoCdc := codec.NewLegacyAmino() + privKey, err := GenerateKey() + require.NoError(t, err) + + pubKey := privKey.PubKey().(*PubKey) + + testCases := []struct { + desc string + msg codec.AminoMarshaler + typ interface{} + expBinary []byte + expJSON string + }{ + { + "ethsecp256k1 private key", + privKey, + &PrivKey{}, + append([]byte{32}, privKey.Bytes()...), // Length-prefixed. + "\"" + base64.StdEncoding.EncodeToString(privKey.Bytes()) + "\"", + }, + { + "ethsecp256k1 public key", + pubKey, + &PubKey{}, + append([]byte{33}, pubKey.Bytes()...), // Length-prefixed. + "\"" + base64.StdEncoding.EncodeToString(pubKey.Bytes()) + "\"", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + // Do a round trip of encoding/decoding binary. + bz, err := aminoCdc.Marshal(tc.msg) + require.NoError(t, err) + require.Equal(t, tc.expBinary, bz) + + err = aminoCdc.Unmarshal(bz, tc.typ) + require.NoError(t, err) + + require.Equal(t, tc.msg, tc.typ) + + // Do a round trip of encoding/decoding JSON. + bz, err = aminoCdc.MarshalJSON(tc.msg) + require.NoError(t, err) + require.Equal(t, tc.expJSON, string(bz)) + + err = aminoCdc.UnmarshalJSON(bz, tc.typ) + require.NoError(t, err) + + require.Equal(t, tc.msg, tc.typ) + }) + } +} diff --git a/eth/crypto/ethsecp256k1/keys.pb.go b/eth/crypto/ethsecp256k1/keys.pb.go new file mode 100644 index 000000000..93ec27286 --- /dev/null +++ b/eth/crypto/ethsecp256k1/keys.pb.go @@ -0,0 +1,500 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ethermint/crypto/v1/ethsecp256k1/keys.proto + +package ethsecp256k1 + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// PubKey defines a type alias for an ecdsa.PublicKey that implements +// Tendermint's PubKey interface. It represents the 33-byte compressed public +// key format. +type PubKey struct { + // key is the public key in byte form + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *PubKey) Reset() { *m = PubKey{} } +func (*PubKey) ProtoMessage() {} +func (*PubKey) Descriptor() ([]byte, []int) { + return fileDescriptor_0c10cadcf35beb64, []int{0} +} +func (m *PubKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PubKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PubKey.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PubKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_PubKey.Merge(m, src) +} +func (m *PubKey) XXX_Size() int { + return m.Size() +} +func (m *PubKey) XXX_DiscardUnknown() { + xxx_messageInfo_PubKey.DiscardUnknown(m) +} + +var xxx_messageInfo_PubKey proto.InternalMessageInfo + +func (m *PubKey) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +// PrivKey defines a type alias for an ecdsa.PrivateKey that implements +// Tendermint's PrivateKey interface. +type PrivKey struct { + // key is the private key in byte form + Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *PrivKey) Reset() { *m = PrivKey{} } +func (m *PrivKey) String() string { return proto.CompactTextString(m) } +func (*PrivKey) ProtoMessage() {} +func (*PrivKey) Descriptor() ([]byte, []int) { + return fileDescriptor_0c10cadcf35beb64, []int{1} +} +func (m *PrivKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PrivKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PrivKey.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PrivKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_PrivKey.Merge(m, src) +} +func (m *PrivKey) XXX_Size() int { + return m.Size() +} +func (m *PrivKey) XXX_DiscardUnknown() { + xxx_messageInfo_PrivKey.DiscardUnknown(m) +} + +var xxx_messageInfo_PrivKey proto.InternalMessageInfo + +func (m *PrivKey) GetKey() []byte { + if m != nil { + return m.Key + } + return nil +} + +func init() { + proto.RegisterType((*PubKey)(nil), "ethermint.crypto.v1.ethsecp256k1.PubKey") + proto.RegisterType((*PrivKey)(nil), "ethermint.crypto.v1.ethsecp256k1.PrivKey") +} + +func init() { + proto.RegisterFile("ethermint/crypto/v1/ethsecp256k1/keys.proto", fileDescriptor_0c10cadcf35beb64) +} + +var fileDescriptor_0c10cadcf35beb64 = []byte{ + // 197 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0x2d, 0xc9, 0x48, + 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x2f, 0x33, 0xd4, + 0x4f, 0x2d, 0xc9, 0x28, 0x4e, 0x4d, 0x2e, 0x30, 0x32, 0x35, 0xcb, 0x36, 0xd4, 0xcf, 0x4e, 0xad, + 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52, 0x80, 0x2b, 0xd6, 0x83, 0x28, 0xd6, 0x2b, + 0x33, 0xd4, 0x43, 0x56, 0x2c, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xac, 0x0f, 0x62, 0x41, + 0xf4, 0x29, 0x29, 0x70, 0xb1, 0x05, 0x94, 0x26, 0x79, 0xa7, 0x56, 0x0a, 0x09, 0x70, 0x31, 0x67, + 0xa7, 0x56, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x04, 0x81, 0x98, 0x56, 0x2c, 0x33, 0x16, 0xc8, + 0x33, 0x28, 0x49, 0x73, 0xb1, 0x07, 0x14, 0x65, 0x96, 0x61, 0x55, 0xe2, 0xe4, 0x71, 0xe2, 0x91, + 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, + 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x7a, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, + 0xc9, 0xf9, 0xb9, 0xfa, 0xa9, 0x65, 0xb9, 0xf9, 0xc5, 0x50, 0xb2, 0xcc, 0xd0, 0x0c, 0xe6, 0x1d, + 0x64, 0xe7, 0x25, 0xb1, 0x81, 0xdd, 0x63, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x03, 0x69, 0xeb, + 0xbb, 0xf6, 0x00, 0x00, 0x00, +} + +func (m *PubKey) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PubKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PubKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PrivKey) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PrivKey) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PrivKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintKeys(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintKeys(dAtA []byte, offset int, v uint64) int { + offset -= sovKeys(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PubKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKeys(uint64(l)) + } + return n +} + +func (m *PrivKey) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovKeys(uint64(l)) + } + return n +} + +func sovKeys(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozKeys(x uint64) (n int) { + return sovKeys(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PubKey) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PubKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PubKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKeys(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKeys + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PrivKey) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PrivKey: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PrivKey: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowKeys + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthKeys + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthKeys + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipKeys(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthKeys + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipKeys(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowKeys + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthKeys + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupKeys + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthKeys + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthKeys = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowKeys = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupKeys = fmt.Errorf("proto: unexpected end of group") +) diff --git a/eth/crypto/hd/algorithm.go b/eth/crypto/hd/algorithm.go new file mode 100644 index 000000000..f4370c387 --- /dev/null +++ b/eth/crypto/hd/algorithm.go @@ -0,0 +1,112 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package hd + +import ( + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/chaincfg" + bip39 "github.com/tyler-smith/go-bip39" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" +) + +const ( + // EthSecp256k1Type defines the ECDSA secp256k1 used on Ethereum + EthSecp256k1Type = hd.PubKeyType(ethsecp256k1.KeyType) +) + +var ( + // SupportedAlgorithms defines the list of signing algorithms used: + // - eth_secp256k1 (Ethereum) + // - secp256k1 (Tendermint) + SupportedAlgorithms = keyring.SigningAlgoList{EthSecp256k1, hd.Secp256k1} + // SupportedAlgorithmsLedger defines the list of signing algorithms used on the Ledger device: + // - eth_secp256k1 (Ethereum) + // - secp256k1 (Tendermint) + SupportedAlgorithmsLedger = keyring.SigningAlgoList{EthSecp256k1, hd.Secp256k1} +) + +// EthSecp256k1Option defines a function keys options for the ethereum Secp256k1 curve. +// It supports eth_secp256k1 and secp256k1 keys for accounts. +func EthSecp256k1Option() keyring.Option { + return func(options *keyring.Options) { + options.SupportedAlgos = SupportedAlgorithms + options.SupportedAlgosLedger = SupportedAlgorithmsLedger + } +} + +var ( + _ keyring.SignatureAlgo = EthSecp256k1 + + // EthSecp256k1 uses the Bitcoin secp256k1 ECDSA parameters. + EthSecp256k1 = ethSecp256k1Algo{} +) + +type ethSecp256k1Algo struct{} + +// Name returns eth_secp256k1 +func (s ethSecp256k1Algo) Name() hd.PubKeyType { + return EthSecp256k1Type +} + +// Derive derives and returns the eth_secp256k1 private key for the given mnemonic and HD path. +func (s ethSecp256k1Algo) Derive() hd.DeriveFn { + return func(mnemonic, bip39Passphrase, path string) ([]byte, error) { + hdpath, err := accounts.ParseDerivationPath(path) + if err != nil { + return nil, err + } + + seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase) + if err != nil { + return nil, err + } + + // create a BTC-utils hd-derivation key chain + masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) + if err != nil { + return nil, err + } + + key := masterKey + for _, n := range hdpath { + key, err = key.Derive(n) + if err != nil { + return nil, err + } + } + + // btc-utils representation of a secp256k1 private key + privateKey, err := key.ECPrivKey() + if err != nil { + return nil, err + } + + // cast private key to a convertible form (single scalar field element of secp256k1) + // and then load into ethcrypto private key format. + // TODO: add links to godocs of the two methods or implementations of them, to compare equivalency + privateKeyECDSA := privateKey.ToECDSA() + derivedKey := crypto.FromECDSA(privateKeyECDSA) + + return derivedKey, nil + } +} + +// Generate generates a eth_secp256k1 private key from the given bytes. +func (s ethSecp256k1Algo) Generate() hd.GenerateFn { + return func(bz []byte) cryptotypes.PrivKey { + bzArr := make([]byte, ethsecp256k1.PrivKeySize) + copy(bzArr, bz) + + // TODO: modulo P + return ðsecp256k1.PrivKey{ + Key: bzArr, + } + } +} diff --git a/eth/crypto/hd/algorithm_test.go b/eth/crypto/hd/algorithm_test.go new file mode 100644 index 000000000..23eee7ae7 --- /dev/null +++ b/eth/crypto/hd/algorithm_test.go @@ -0,0 +1,132 @@ +package hd + +import ( + "os" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + + amino "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + + "github.com/NibiruChain/nibiru/v2/eth" + cryptocodec "github.com/NibiruChain/nibiru/v2/eth/crypto/codec" + enccodec "github.com/NibiruChain/nibiru/v2/eth/encoding/codec" +) + +var TestCodec amino.Codec + +func init() { + cdc := amino.NewLegacyAmino() + cryptocodec.RegisterCrypto(cdc) + + interfaceRegistry := types.NewInterfaceRegistry() + TestCodec = amino.NewProtoCodec(interfaceRegistry) + enccodec.RegisterInterfaces(interfaceRegistry) +} + +const ( + mnemonic = "picnic rent average infant boat squirrel federal assault mercy purity very motor fossil wheel verify upset box fresh horse vivid copy predict square regret" + + // hdWalletFixEnv defines whether the standard (correct) bip39 + // derivation path was used, or if derivation was affected by + // https://github.com/btcsuite/btcutil/issues/179 + hdWalletFixEnv = "GO_ETHEREUM_HDWALLET_FIX_ISSUE_179" +) + +const appName = "nibid" + +func TestKeyring(t *testing.T) { + dir := t.TempDir() + mockIn := strings.NewReader("") + kr, err := keyring.New(appName, keyring.BackendTest, dir, mockIn, TestCodec, EthSecp256k1Option()) + require.NoError(t, err) + + // fail in retrieving key + info, err := kr.Key("foo") + require.Error(t, err) + require.Nil(t, info) + + mockIn.Reset("password\npassword\n") + info, mnemonic, err := kr.NewMnemonic("foo", keyring.English, eth.BIP44HDPath, keyring.DefaultBIP39Passphrase, EthSecp256k1) + require.NoError(t, err) + require.NotEmpty(t, mnemonic) + require.Equal(t, "foo", info.Name) + require.Equal(t, "local", info.GetType().String()) + pubKey, err := info.GetPubKey() + require.NoError(t, err) + require.Equal(t, string(EthSecp256k1Type), pubKey.Type()) + + hdPath := eth.BIP44HDPath + + bz, err := EthSecp256k1.Derive()(mnemonic, keyring.DefaultBIP39Passphrase, hdPath) + require.NoError(t, err) + require.NotEmpty(t, bz) + + wrongBz, err := EthSecp256k1.Derive()(mnemonic, keyring.DefaultBIP39Passphrase, "/wrong/hdPath") + require.Error(t, err) + require.Empty(t, wrongBz) + + privkey := EthSecp256k1.Generate()(bz) + addr := common.BytesToAddress(privkey.PubKey().Address().Bytes()) + + os.Setenv(hdWalletFixEnv, "true") + wallet, err := NewFromMnemonic(mnemonic) + os.Setenv(hdWalletFixEnv, "") + require.NoError(t, err) + + path := MustParseDerivationPath(hdPath) + + account, err := wallet.Derive(path, false) + require.NoError(t, err) + require.Equal(t, addr.String(), account.Address.String()) +} + +func TestDerivation(t *testing.T) { + bz, err := EthSecp256k1.Derive()(mnemonic, keyring.DefaultBIP39Passphrase, eth.BIP44HDPath) + require.NoError(t, err) + require.NotEmpty(t, bz) + + badBz, err := EthSecp256k1.Derive()(mnemonic, keyring.DefaultBIP39Passphrase, "44'/60'/0'/0/0") + require.NoError(t, err) + require.NotEmpty(t, badBz) + + require.NotEqual(t, bz, badBz) + + privkey := EthSecp256k1.Generate()(bz) + badPrivKey := EthSecp256k1.Generate()(badBz) + + require.False(t, privkey.Equals(badPrivKey)) + + wallet, err := NewFromMnemonic(mnemonic) + require.NoError(t, err) + + path := MustParseDerivationPath(eth.BIP44HDPath) + account, err := wallet.Derive(path, false) + require.NoError(t, err) + + badPath := MustParseDerivationPath("44'/60'/0'/0/0") + badAccount, err := wallet.Derive(badPath, false) + require.NoError(t, err) + + // Equality of Address BIP44 + require.Equal(t, account.Address.String(), "0xA588C66983a81e800Db4dF74564F09f91c026351") + require.Equal(t, badAccount.Address.String(), "0xF8D6FDf2B8b488ea37e54903750dcd13F67E71cb") + // Inequality of wrong derivation path address + require.NotEqual(t, account.Address.String(), badAccount.Address.String()) + // Equality of impls between Ethereum and Nibiru + require.Equal(t, common.BytesToAddress(privkey.PubKey().Address().Bytes()).String(), "0xA588C66983a81e800Db4dF74564F09f91c026351") + require.Equal(t, common.BytesToAddress(badPrivKey.PubKey().Address().Bytes()).String(), "0xF8D6FDf2B8b488ea37e54903750dcd13F67E71cb") + + // Equality of impls between Ethereum and Nibiru + require.Equal(t, common.BytesToAddress(privkey.PubKey().Address()).String(), account.Address.String()) + require.Equal(t, common.BytesToAddress(badPrivKey.PubKey().Address()).String(), badAccount.Address.String()) + + // Inequality of wrong derivation path + require.NotEqual(t, common.BytesToAddress(privkey.PubKey().Address()).String(), badAccount.Address.String()) + require.NotEqual(t, common.BytesToAddress(badPrivKey.PubKey().Address()).String(), account.Address.Hex()) +} diff --git a/eth/crypto/hd/benchmark_test.go b/eth/crypto/hd/benchmark_test.go new file mode 100644 index 000000000..976cf6f87 --- /dev/null +++ b/eth/crypto/hd/benchmark_test.go @@ -0,0 +1,32 @@ +package hd + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +func BenchmarkEthSecp256k1Algo_Derive(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + deriveFn := EthSecp256k1.Derive() + if _, err := deriveFn(mnemonic, keyring.DefaultBIP39Passphrase, eth.BIP44HDPath); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkEthSecp256k1Algo_Generate(b *testing.B) { + bz, err := EthSecp256k1.Derive()(mnemonic, keyring.DefaultBIP39Passphrase, eth.BIP44HDPath) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + (ðSecp256k1Algo{}).Generate()(bz) + } +} diff --git a/eth/crypto/hd/utils_test.go b/eth/crypto/hd/utils_test.go new file mode 100644 index 000000000..2082368d8 --- /dev/null +++ b/eth/crypto/hd/utils_test.go @@ -0,0 +1,181 @@ +// NOTE: This code is being used as test helper functions. +package hd + +import ( + "crypto/ecdsa" + "errors" + "os" + "sync" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/chaincfg" + bip39 "github.com/tyler-smith/go-bip39" +) + +const issue179FixEnvar = "GO_ETHEREUM_HDWALLET_FIX_ISSUE_179" + +// Wallet is the underlying wallet struct. +type Wallet struct { + mnemonic string + masterKey *hdkeychain.ExtendedKey + seed []byte + paths map[common.Address]accounts.DerivationPath + accounts []accounts.Account + stateLock sync.RWMutex + fixIssue172 bool +} + +// NewFromMnemonic returns a new wallet from a BIP-39 mnemonic. +func NewFromMnemonic(mnemonic string) (*Wallet, error) { + if mnemonic == "" { + return nil, errors.New("mnemonic is required") + } + + if !bip39.IsMnemonicValid(mnemonic) { + return nil, errors.New("mnemonic is invalid") + } + + seed, err := NewSeedFromMnemonic(mnemonic) + if err != nil { + return nil, err + } + + wallet, err := newWallet(seed) + if err != nil { + return nil, err + } + wallet.mnemonic = mnemonic + + return wallet, nil +} + +// NewSeedFromMnemonic returns a BIP-39 seed based on a BIP-39 mnemonic. +func NewSeedFromMnemonic(mnemonic string) ([]byte, error) { + if mnemonic == "" { + return nil, errors.New("mnemonic is required") + } + + return bip39.NewSeedWithErrorChecking(mnemonic, "") +} + +func newWallet(seed []byte) (*Wallet, error) { + masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) + if err != nil { + return nil, err + } + + return &Wallet{ + masterKey: masterKey, + seed: seed, + accounts: []accounts.Account{}, + paths: map[common.Address]accounts.DerivationPath{}, + fixIssue172: false || len(os.Getenv(issue179FixEnvar)) > 0, + }, nil +} + +// Derive implements accounts.Wallet, deriving a new account at the specific +// derivation path. If pin is set to true, the account will be added to the list +// of tracked accounts. +func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { + // Try to derive the actual account and update its URL if successful + w.stateLock.RLock() // Avoid device disappearing during derivation + + address, err := w.deriveAddress(path) + + w.stateLock.RUnlock() + + // If an error occurred or no pinning was requested, return + if err != nil { + return accounts.Account{}, err + } + + account := accounts.Account{ + Address: address, + URL: accounts.URL{ + Scheme: "", + Path: path.String(), + }, + } + + if !pin { + return account, nil + } + + // Pinning needs to modify the state + w.stateLock.Lock() + defer w.stateLock.Unlock() + + if _, ok := w.paths[address]; !ok { + w.accounts = append(w.accounts, account) + w.paths[address] = path + } + + return account, nil +} + +// MustParseDerivationPath parses the derivation path in string format into +// []uint32 but will panic if it can't parse it. +func MustParseDerivationPath(path string) accounts.DerivationPath { + parsed, err := accounts.ParseDerivationPath(path) + if err != nil { + panic(err) + } + + return parsed +} + +// DerivePrivateKey derives the private key of the derivation path. +func (w *Wallet) derivePrivateKey(path accounts.DerivationPath) (*ecdsa.PrivateKey, error) { + var err error + key := w.masterKey + for _, n := range path { + if w.fixIssue172 && key.IsAffectedByIssue172() { + key, err = key.Derive(n) + } else { + //lint:ignore SA1019 this is used for testing only + key, err = key.DeriveNonStandard(n) //nolint:staticcheck + } + if err != nil { + return nil, err + } + } + + privateKey, err := key.ECPrivKey() + privateKeyECDSA := privateKey.ToECDSA() + if err != nil { + return nil, err + } + + return privateKeyECDSA, nil +} + +// derivePublicKey derives the public key of the derivation path. +func (w *Wallet) derivePublicKey(path accounts.DerivationPath) (*ecdsa.PublicKey, error) { + privateKeyECDSA, err := w.derivePrivateKey(path) + if err != nil { + return nil, err + } + + publicKey := privateKeyECDSA.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, errors.New("failed to get public key") + } + + return publicKeyECDSA, nil +} + +// DeriveAddress derives the account address of the derivation path. +func (w *Wallet) deriveAddress(path accounts.DerivationPath) (common.Address, error) { + publicKeyECDSA, err := w.derivePublicKey(path) + if err != nil { + return common.Address{}, err + } + + address := crypto.PubkeyToAddress(*publicKeyECDSA) + return address, nil +} diff --git a/eth/crypto/keyring/options.go b/eth/crypto/keyring/options.go new file mode 100644 index 000000000..86c384db2 --- /dev/null +++ b/eth/crypto/keyring/options.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023-2024 Nibi, Inc. + +package keyring + +import ( + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/types" + + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" + "github.com/NibiruChain/nibiru/v2/eth/crypto/hd" +) + +// AppName defines the Ledger app used for signing. +const AppName = "Ethereum" + +var ( + // SupportedAlgorithms defines the list of signing algorithms used on Nibiru: + // - eth_secp256k1 (Ethereum) + SupportedAlgorithms = keyring.SigningAlgoList{hd.EthSecp256k1} + // SupportedAlgorithmsLedger defines the list of signing algorithms used on + // Nibiru for the Ledger device: + // - secp256k1 (in order to comply with Cosmos SDK) + // The Ledger derivation function is responsible for all signing and address generation. + SupportedAlgorithmsLedger = keyring.SigningAlgoList{hd.EthSecp256k1} + // CreatePubkey uses the ethsecp256k1 pubkey with Ethereum address generation and keccak hashing + CreatePubkey = func(key []byte) types.PubKey { return ðsecp256k1.PubKey{Key: key} } + // SkipDERConversion represents whether the signed Ledger output should skip conversion from DER to BER. + // This is set to true for signing performed by the Ledger Ethereum app. + SkipDERConversion = true +) + +// EthSecp256k1Option defines a function keys options for the ethereum Secp256k1 curve. +// It supports eth_secp256k1 keys for accounts. +func Option() keyring.Option { + return func(options *keyring.Options) { + options.SupportedAlgos = SupportedAlgorithms + options.SupportedAlgosLedger = SupportedAlgorithmsLedger + options.LedgerCreateKey = CreatePubkey + options.LedgerAppName = AppName + options.LedgerSigSkipDERConv = SkipDERConversion + } +} diff --git a/eth/crypto/secp256r1/verify.go b/eth/crypto/secp256r1/verify.go new file mode 100644 index 000000000..9b4ba8685 --- /dev/null +++ b/eth/crypto/secp256r1/verify.go @@ -0,0 +1,52 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package secp256r1 + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "math/big" +) + +// Verifies the given signature (r, s) for the given hash and public key (x, y). +func Verify(hash []byte, r, s, x, y *big.Int) bool { + // Create the public key format + publicKey := newECDSAPublicKey(x, y) + + // Check if they are invalid public key coordinates + if publicKey == nil { + return false + } + + // Verify the signature with the public key, + // then return true if it's valid, false otherwise + return ecdsa.Verify(publicKey, hash, r, s) +} + +// newECDSAPublicKey creates an ECDSA P256 public key from the given coordinates +func newECDSAPublicKey(x, y *big.Int) *ecdsa.PublicKey { + // Check if the given coordinates are valid and in the reference point (infinity) + if x == nil || y == nil || x.Sign() == 0 && y.Sign() == 0 || !elliptic.P256().IsOnCurve(x, y) { + return nil + } + + return &ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: x, + Y: y, + } +} diff --git a/eth/eip55.go b/eth/eip55.go new file mode 100644 index 000000000..2c801bbea --- /dev/null +++ b/eth/eip55.go @@ -0,0 +1,85 @@ +package eth + +import ( + "encoding/json" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" +) + +var _ sdk.CustomProtobufType = (*EIP55Addr)(nil) + +// EIP55Addr is a wrapper around gethcommon.Address that provides JSON marshaling +// and unmarshalling as well as Protobuf serialization and deserialization. +// The constructors ensure that the input string is a valid 20 byte hex address. +type EIP55Addr struct { + gethcommon.Address +} + +// Checks input length, but not case-sensitive hex. +func NewEIP55AddrFromStr(input string) (EIP55Addr, error) { + if !gethcommon.IsHexAddress(input) { + return EIP55Addr{}, fmt.Errorf( + "EIP55AddrError: input \"%s\" is not an ERC55-compliant, 20 byte hex address", + input, + ) + } + + addr := EIP55Addr{ + Address: gethcommon.HexToAddress(input), + } + + return addr, nil +} + +// Marshal implements the gogo proto custom type interface. +// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md +func (h EIP55Addr) Marshal() ([]byte, error) { + return h.Bytes(), nil +} + +// MarshalJSON returns the [EIP55Addr] as JSON bytes. +// Implements the gogo proto custom type interface. +// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md +func (h EIP55Addr) MarshalJSON() ([]byte, error) { + return json.Marshal(h.String()) +} + +// MarshalTo serializes a EIP55Addr directly into a pre-allocated byte slice ("data"). +// MarshalTo implements the gogo proto custom type interface. +// Implements the gogo proto custom type interface. +// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md +func (h *EIP55Addr) MarshalTo(data []byte) (n int, err error) { + copy(data, h.Bytes()) + return h.Size(), nil +} + +// Unmarshal implements the gogo proto custom type interface. +// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md +func (h *EIP55Addr) Unmarshal(data []byte) error { + addr := gethcommon.BytesToAddress(data) + *h = EIP55Addr{Address: addr} + return nil +} + +// UnmarshalJSON implements the gogo proto custom type interface. +// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md +func (h *EIP55Addr) UnmarshalJSON(bz []byte) error { + var addrStr string + if err := json.Unmarshal(bz, &addrStr); err != nil { + return err + } + addr, err := NewEIP55AddrFromStr(addrStr) + if err != nil { + return err + } + *h = addr + return nil +} + +// Size implements the gogo proto custom type interface. +// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md +func (h EIP55Addr) Size() int { + return len(h.Bytes()) +} diff --git a/eth/eip55_test.go b/eth/eip55_test.go new file mode 100644 index 000000000..af56344a4 --- /dev/null +++ b/eth/eip55_test.go @@ -0,0 +1,233 @@ +package eth_test + +import ( + "encoding/json" + "fmt" + "strconv" + "testing" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// mustNewEIP55AddrFromStr is the same as [NewEIP55AddrFromStr], except it panics +// when there's an error. +func mustNewEIP55AddrFromStr(input string) eth.EIP55Addr { + addr, err := eth.NewEIP55AddrFromStr(input) + if err != nil { + panic(err) + } + return addr +} + +func (s *EIP55AddrSuite) TestEquivalence() { + expectedGethAddr := gethcommon.HexToAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed") + expectedEIP55Addr := mustNewEIP55AddrFromStr("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed") + + equivalentAddrs := []string{ + "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + "0x5AAEB6053F3E94C9B9A09F33669435E7EF1BEAED", + "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + "0X5AAEB6053F3E94C9B9A09F33669435E7EF1BEAED", + } + + for _, addr := range equivalentAddrs { + eip55Addr, err := eth.NewEIP55AddrFromStr(addr) + s.Require().NoError(err) + + s.Equal(expectedEIP55Addr, eip55Addr) + s.Equal(expectedGethAddr, eip55Addr.Address) + } +} + +// TestEIP55Addr_NewEIP55Addr: Test to showcase the flexibility of inputs that can be +// passed to `eth.NewEIP55AddrFromStr` and result in a "valid" `EIP55Addr` that preserves +// bijectivity with `gethcommon.Address` and has a canonical string +// representation. +// +// We only want to store valid `EIP55Addr` strings in state. Hex addresses that +// include or remove the prefix, or change the letters to and from lower and +// upper case will all produce the same `EIP55Addr` when passed to +// `eth.NewEIP55AddrFromStr`. +func (s *EIP55AddrSuite) TestNewEIP55Addr() { + // TestCase: An instance of a "EIP55Addr" that derives to the + // expected Ethereum address and results in the same string representation. + type TestCase struct { + input string + name string + wantErr bool + } + + want := "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" + + for _, tc := range []TestCase{ + { + input: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + name: "happy: no-op (sanity check to show constructor doesn't break a valid input)", + }, + { + input: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + name: "happy: lower case is valid", + }, + { + input: "0x5AAEB6053F3E94C9B9A09F33669435E7EF1BEAED", + name: "happy: upper case is valid", + }, + { + input: "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + name: "happy: 0x prefix: missing", + }, + { + input: "0X5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + name: "happy: 0X prefix: typo", + }, + { + input: "nibi1zaa12312312aacbcbeabea123", + name: "sad: bech32 is not hex", + wantErr: true, + }, + } { + tc := tc + s.Run(tc.name, func() { + got, err := eth.NewEIP55AddrFromStr(tc.input) + if tc.wantErr { + s.Require().Error(err) + return + } + + // string input should give the canonical EIP55Addr + s.Equal(want, got.String()) + s.Equal(gethcommon.HexToAddress(tc.input), got.Address) + }) + } +} + +func (s *EIP55AddrSuite) TestJsonEncoding() { + for tcIdx, tc := range []struct { + input eth.EIP55Addr + expectedJson json.RawMessage + wantErr string + }{ + { + input: mustNewEIP55AddrFromStr("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"), + expectedJson: []byte("\"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed\""), + }, + { + input: mustNewEIP55AddrFromStr("0xAe967917c465db8578ca9024c205720b1a3651A9"), + expectedJson: []byte("\"0xAe967917c465db8578ca9024c205720b1a3651A9\""), + }, + { + input: mustNewEIP55AddrFromStr("0x1111111111111111111112222222222223333323"), + expectedJson: []byte("\"0x1111111111111111111112222222222223333323\""), + }, + } { + s.Run(strconv.Itoa(tcIdx), func() { + jsonBz, err := tc.input.MarshalJSON() + s.Require().NoError(err) + s.Require().EqualValues(tc.expectedJson, jsonBz) + + eip55Addr := new(eth.EIP55Addr) + s.Require().NoError(eip55Addr.UnmarshalJSON(jsonBz)) + s.Require().EqualValues(tc.input, *eip55Addr) + }) + } +} + +func (s *EIP55AddrSuite) TestProtobufEncoding() { + for tcIdx, tc := range []struct { + input eth.EIP55Addr + expectedProtoBz []byte + wantErr string + }{ + { + input: mustNewEIP55AddrFromStr("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"), + expectedProtoBz: []byte{90, 174, 182, 5, 63, 62, 148, 201, 185, 160, 159, 51, 102, 148, 53, 231, 239, 27, 234, 237}, + }, + { + input: mustNewEIP55AddrFromStr("0xAe967917c465db8578ca9024c205720b1a3651A9"), + expectedProtoBz: []byte{174, 150, 121, 23, 196, 101, 219, 133, 120, 202, 144, 36, 194, 5, 114, 11, 26, 54, 81, 169}, + }, + { + input: mustNewEIP55AddrFromStr("0x1111111111111111111112222222222223333323"), + expectedProtoBz: []byte{17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 34, 34, 34, 34, 34, 35, 51, 51, 35}, + }, + } { + s.Run(strconv.Itoa(tcIdx), func() { + bz, err := tc.input.Marshal() + s.Require().NoError(err) + s.Require().EqualValues(tc.expectedProtoBz, bz) + + eip55Addr := new(eth.EIP55Addr) + s.Require().NoError(eip55Addr.Unmarshal(bz)) + s.Require().Equal(tc.input.Address, eip55Addr.Address) + }) + } +} + +func (s *EIP55AddrSuite) TestSize() { + for idx, tc := range []struct { + input eth.EIP55Addr + expectedSize int + wantErr string + }{ + { + input: mustNewEIP55AddrFromStr("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"), + expectedSize: 20, + }, + { + input: mustNewEIP55AddrFromStr("0xAe967917c465db8578ca9024c205720b1a3651A9"), + expectedSize: 20, + }, + { + input: mustNewEIP55AddrFromStr("0x1111111111111111111112222222222223333323"), + expectedSize: 20, + }, + } { + s.Run(strconv.Itoa(idx), func() { + s.Require().EqualValues(tc.expectedSize, tc.input.Size()) + }) + } +} + +// showcases how geth checks for valid hex addresses and treats invalid inputs +func (s *EIP55AddrSuite) TestHexAddress() { + s.True(gethcommon.IsHexAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")) + s.True(gethcommon.IsHexAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAED")) + s.False(gethcommon.IsHexAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed1234")) + s.False(gethcommon.IsHexAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1B")) + + s.Equal("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", gethcommon.HexToAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed").Hex()) + s.Equal("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", gethcommon.HexToAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAED").Hex()) + s.Equal("0xb6053f3e94c9B9a09f33669435e7eF1BEAEd1234", gethcommon.HexToAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed1234").Hex()) + s.Equal("0x00005AaEb6053f3e94c9b9A09f33669435e7Ef1b", gethcommon.HexToAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1B").Hex()) +} + +type EIP55AddrSuite struct { + suite.Suite +} + +func TestEIP55AddrSuite(t *testing.T) { + suite.Run(t, new(EIP55AddrSuite)) +} + +func (s *EIP55AddrSuite) TestStringEncoding() { + addrHex := "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" + addr, err := eth.NewEIP55AddrFromStr(addrHex) + s.Require().NoError(err) + s.Require().Equal(addrHex, addr.Address.Hex()) + + addrBz, err := addr.Marshal() + s.Require().NoError(err) + s.Require().EqualValues(addr.Bytes(), addrBz) + + bz, err := addr.MarshalJSON() + s.Require().NoError(err) + s.Require().Equal(fmt.Sprintf(`"%s"`, addrHex), string(bz)) + + newAddr := new(eth.EIP55Addr) + err = newAddr.UnmarshalJSON(bz) + s.Require().NoError(err) + s.Require().EqualValues(addrHex, newAddr.Hex()) +} diff --git a/eth/eip712/domain.go b/eth/eip712/domain.go new file mode 100644 index 000000000..287ddb1f4 --- /dev/null +++ b/eth/eip712/domain.go @@ -0,0 +1,20 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eip712 + +import ( + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/signer/core/apitypes" +) + +// createEIP712Domain creates the typed data domain for the given chainID. +func createEIP712Domain(chainID uint64) apitypes.TypedDataDomain { + domain := apitypes.TypedDataDomain{ + Name: "Cosmos Web3", + Version: "1.0.0", + ChainId: math.NewHexOrDecimal256(int64(chainID)), // #nosec G701 + VerifyingContract: "cosmos", + Salt: "0", + } + + return domain +} diff --git a/eth/eip712/eip712.go b/eth/eip712/eip712.go new file mode 100644 index 000000000..2a55524d3 --- /dev/null +++ b/eth/eip712/eip712.go @@ -0,0 +1,35 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eip712 + +import ( + "github.com/ethereum/go-ethereum/signer/core/apitypes" +) + +// WrapTxToTypedData wraps an Amino-encoded Cosmos Tx JSON SignDoc +// bytestream into an EIP712-compatible TypedData request. +func WrapTxToTypedData( + chainID uint64, + data []byte, +) (apitypes.TypedData, error) { + messagePayload, err := createEIP712MessagePayload(data) + message := messagePayload.message + if err != nil { + return apitypes.TypedData{}, err + } + + types, err := createEIP712Types(messagePayload) + if err != nil { + return apitypes.TypedData{}, err + } + + domain := createEIP712Domain(chainID) + + typedData := apitypes.TypedData{ + Types: types, + PrimaryType: txField, + Domain: domain, + Message: message, + } + + return typedData, nil +} diff --git a/eth/eip712/eip712_fuzzer_test.go b/eth/eip712/eip712_fuzzer_test.go new file mode 100644 index 000000000..a7e096eae --- /dev/null +++ b/eth/eip712/eip712_fuzzer_test.go @@ -0,0 +1,194 @@ +package eip712_test + +import ( + "fmt" + "strings" + + rand "github.com/cometbft/cometbft/libs/rand" + + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" + + "github.com/NibiruChain/nibiru/v2/eth/eip712" +) + +type EIP712FuzzTestParams struct { + numTestObjects int + maxNumFieldsPerObject int + minStringLength int + maxStringLength int + randomFloatRange float64 + maxArrayLength int + maxObjectDepth int +} + +const ( + numPrimitiveJSONTypes = 3 + numJSONTypes = 5 + asciiRangeStart = 65 + asciiRangeEnd = 127 + fuzzTestName = "Flatten" +) + +const ( + jsonBoolType = iota + jsonStringType = iota + jsonFloatType = iota + jsonArrayType = iota + jsonObjectType = iota +) + +var params = EIP712FuzzTestParams{ + numTestObjects: 16, + maxNumFieldsPerObject: 16, + minStringLength: 16, + maxStringLength: 48, + randomFloatRange: 120000000, + maxArrayLength: 8, + maxObjectDepth: 4, +} + +// TestRandomPayloadFlattening generates many random payloads with different JSON values to ensure +// that Flattening works across all inputs. +// Note that this is a fuzz test, although it doesn't use Go's Fuzz testing suite, since there are +// variable input sizes, types, and fields. While it may be possible to translate a single input into +// a JSON object, it would require difficult parsing, and ultimately approximates our randomized unit +// tests as they are. +func (s *EIP712TestSuite) TestRandomPayloadFlattening() { + // Re-seed rand generator + rand.Seed(rand.Int64()) + + for i := 0; i < params.numTestObjects; i++ { + s.Run(fmt.Sprintf("%v%d", fuzzTestName, i), func() { + payload := s.generateRandomPayload(i) + + flattened, numMessages, err := eip712.FlattenPayloadMessages(payload) + + s.Require().NoError(err) + s.Require().Equal(numMessages, i) + + s.verifyPayloadAgainstFlattened(payload, flattened) + }) + } +} + +// generateRandomPayload creates a random payload of the desired format, with random sub-objects. +func (s *EIP712TestSuite) generateRandomPayload(numMessages int) gjson.Result { + payload := s.createRandomJSONObject().Raw + msgs := make([]gjson.Result, numMessages) + + for i := 0; i < numMessages; i++ { + msgs[i] = s.createRandomJSONObject() + } + + payload, err := sjson.Set(payload, msgsFieldName, msgs) + s.Require().NoError(err) + + return gjson.Parse(payload) +} + +// createRandomJSONObject creates a JSON object with random fields. +func (s *EIP712TestSuite) createRandomJSONObject() gjson.Result { + var err error + payloadRaw := "" + + numFields := s.createRandomIntInRange(0, params.maxNumFieldsPerObject) + for i := 0; i < numFields; i++ { + key := s.createRandomString() + + randField := s.createRandomJSONField(i, 0) + payloadRaw, err = sjson.Set(payloadRaw, key, randField) + s.Require().NoError(err) + } + + return gjson.Parse(payloadRaw) +} + +// createRandomJSONField creates a random field with a random JSON type, with the possibility of +// nested fields up to depth objects. +func (s *EIP712TestSuite) createRandomJSONField(t int, depth int) interface{} { + switch t % numJSONTypes { + case jsonBoolType: + return s.createRandomBoolean() + case jsonStringType: + return s.createRandomString() + case jsonFloatType: + return s.createRandomFloat() + case jsonArrayType: + return s.createRandomJSONNestedArray(depth) + case jsonObjectType: + return s.createRandomJSONNestedObject(depth) + default: + return nil + } +} + +// createRandomJSONNestedArray creates an array of random nested JSON fields. +func (s *EIP712TestSuite) createRandomJSONNestedArray(depth int) []interface{} { + arr := make([]interface{}, rand.Intn(params.maxArrayLength)) + for i := range arr { + arr[i] = s.createRandomJSONNestedField(depth) + } + + return arr +} + +// createRandomJSONNestedObject creates a key-value set of objects with random nested JSON fields. +func (s *EIP712TestSuite) createRandomJSONNestedObject(depth int) interface{} { + numFields := rand.Intn(params.maxNumFieldsPerObject) + obj := make(map[string]interface{}) + + for i := 0; i < numFields; i++ { + subField := s.createRandomJSONNestedField(depth) + + obj[s.createRandomString()] = subField + } + + return obj +} + +// createRandomJSONNestedField serves as a helper for createRandomJSONField and returns a random +// subfield to populate an array or object type. +func (s *EIP712TestSuite) createRandomJSONNestedField(depth int) interface{} { + var newFieldType int + + if depth == params.maxObjectDepth { + newFieldType = rand.Intn(numPrimitiveJSONTypes) + } else { + newFieldType = rand.Intn(numJSONTypes) + } + + return s.createRandomJSONField(newFieldType, depth+1) +} + +func (s *EIP712TestSuite) createRandomBoolean() bool { + return rand.Intn(2) == 0 +} + +func (s *EIP712TestSuite) createRandomFloat() float64 { + return (rand.Float64() - 0.5) * params.randomFloatRange +} + +func (s *EIP712TestSuite) createRandomString() string { + bzLen := s.createRandomIntInRange(params.minStringLength, params.maxStringLength) + bz := make([]byte, bzLen) + + for i := 0; i < bzLen; i++ { + bz[i] = byte(s.createRandomIntInRange(asciiRangeStart, asciiRangeEnd)) + } + + str := string(bz) + + // Remove control characters, since they will make JSON invalid + str = strings.ReplaceAll(str, "{", "") + str = strings.ReplaceAll(str, "}", "") + str = strings.ReplaceAll(str, "]", "") + str = strings.ReplaceAll(str, "[", "") + + return str +} + +// createRandomIntInRange provides a random integer between [min, max) +func (s *EIP712TestSuite) createRandomIntInRange(min int, max int) int { + return rand.Intn(max-min) + min +} diff --git a/eth/eip712/eip712_legacy.go b/eth/eip712/eip712_legacy.go new file mode 100644 index 000000000..b50f10e5f --- /dev/null +++ b/eth/eip712/eip712_legacy.go @@ -0,0 +1,461 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eip712 + +import ( + "encoding/json" + "fmt" + "math/big" + "reflect" // #nosec G702 for sensitive import + "strings" + "time" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/ethereum/go-ethereum/common" + gethmath "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/signer/core/apitypes" +) + +type FeeDelegationOptions struct { + FeePayer sdk.AccAddress +} + +const ( + typeDefPrefix = "_" +) + +// LegacyWrapTxToTypedData is an ultimate method that wraps Amino-encoded Cosmos +// Tx JSON data into an EIP712-compatible TypedData request. +func LegacyWrapTxToTypedData( + cdc codectypes.AnyUnpacker, + chainID uint64, + msg sdk.Msg, + data []byte, + feeDelegation *FeeDelegationOptions, +) (apitypes.TypedData, error) { + txData := make(map[string]any) + + if err := json.Unmarshal(data, &txData); err != nil { + return apitypes.TypedData{}, errorsmod.Wrap(errortypes.ErrJSONUnmarshal, "failed to JSON unmarshal data") + } + + domain := apitypes.TypedDataDomain{ + Name: "Cosmos Web3", + Version: "1.0.0", + ChainId: gethmath.NewHexOrDecimal256(int64(chainID)), + VerifyingContract: "cosmos", + Salt: "0", + } + + msgTypes, err := extractMsgTypes(cdc, "MsgValue", msg) + if err != nil { + return apitypes.TypedData{}, err + } + + if feeDelegation != nil { + feeInfo, ok := txData["fee"].(map[string]any) + if !ok { + return apitypes.TypedData{}, errorsmod.Wrap(errortypes.ErrInvalidType, "cannot parse fee from tx data") + } + + feeInfo["feePayer"] = feeDelegation.FeePayer.String() + + // also patching msgTypes to include feePayer + msgTypes["Fee"] = []apitypes.Type{ + {Name: "feePayer", Type: "string"}, + {Name: "amount", Type: "Coin[]"}, + {Name: "gas", Type: "string"}, + } + } + + typedData := apitypes.TypedData{ + Types: msgTypes, + PrimaryType: "Tx", + Domain: domain, + Message: txData, + } + + return typedData, nil +} + +func extractMsgTypes(cdc codectypes.AnyUnpacker, msgTypeName string, msg sdk.Msg) (apitypes.Types, error) { + rootTypes := apitypes.Types{ + "EIP712Domain": { + { + Name: "name", + Type: "string", + }, + { + Name: "version", + Type: "string", + }, + { + Name: "chainId", + Type: "uint256", + }, + { + Name: "verifyingContract", + Type: "string", + }, + { + Name: "salt", + Type: "string", + }, + }, + "Tx": { + {Name: "account_number", Type: "string"}, + {Name: "chain_id", Type: "string"}, + {Name: "fee", Type: "Fee"}, + {Name: "memo", Type: "string"}, + {Name: "msgs", Type: "Msg[]"}, + {Name: "sequence", Type: "string"}, + // Note timeout_height was removed because it was not getting filled with the legacyTx + // {Name: "timeout_height", Type: "string"}, + }, + "Fee": { + {Name: "amount", Type: "Coin[]"}, + {Name: "gas", Type: "string"}, + }, + "Coin": { + {Name: "denom", Type: "string"}, + {Name: "amount", Type: "string"}, + }, + "Msg": { + {Name: "type", Type: "string"}, + {Name: "value", Type: msgTypeName}, + }, + msgTypeName: {}, + } + + if err := walkFields(cdc, rootTypes, msgTypeName, msg); err != nil { + return nil, err + } + + return rootTypes, nil +} + +func walkFields(cdc codectypes.AnyUnpacker, typeMap apitypes.Types, rootType string, in any) (err error) { + defer doRecover(&err) + + t := reflect.TypeOf(in) + v := reflect.ValueOf(in) + + for { + if t.Kind() == reflect.Ptr || + t.Kind() == reflect.Interface { + t = t.Elem() + v = v.Elem() + + continue + } + + break + } + + return legacyTraverseFields(cdc, typeMap, rootType, typeDefPrefix, t, v) +} + +type CosmosAnyWrapper struct { + Type string `json:"type"` + Value any `json:"value"` +} + +// legacyTraverseFields: Recursively inspects the fields of a given +// `reflect.Type` (t) and `reflect.Value`(v) and maps them to an +// Ethereum-compatible type description compliant with EIP-712. For operations +// like EIP-712 signing, complex Go structs need to be translated into a flat +// list of types that can be understood in Ethereum's type system. +func legacyTraverseFields( + // cdc: A codec capable of unpackaing protobuf + // `"github.com/cosmos/cosmos-sdk/codec/types".Any` types into Go + // structs. + cdc codectypes.AnyUnpacker, + // typeMap: map storing type descriptions + typeMap apitypes.Types, + // rootType: name of the root type processed + rootType string, + // prefix: Namespace prefix to avoid name collisions in `typeMap` + prefix string, + // t: reflect type of the data to process + t reflect.Type, + // v: reflect value of the data to process + v reflect.Value, +) error { + // Setup: Check that the number of fields in `typeMap` for the `rootType` + // or a sanitized version of `prefix` matches the number of fields in + // type `t`. If they match, the type has already been processed, so we + // return early. + numFieldsT := t.NumField() + if prefix == typeDefPrefix { + if len(typeMap[rootType]) == numFieldsT { + return nil + } + } else { + typeDef := sanitizeTypedef(prefix) + if len(typeMap[typeDef]) == numFieldsT { + return nil + } + } + + // Field Iteration: Iterate over each field of tpye `t`, + // (1) extracting the type and value of the field, + // (2) unpacking in the event the field is an `Any`, + // (3) and skipping empty fields. + // INFO: If a field is a struct, unpack each field recursively to handle + // nested data structures. + for fieldIdx := 0; fieldIdx < numFieldsT; fieldIdx++ { + var ( + field reflect.Value + err error + ) + + if v.IsValid() { + field = v.Field(fieldIdx) + } + + fieldType := t.Field(fieldIdx).Type + fieldName := jsonNameFromTag(t.Field(fieldIdx).Tag) + + if fieldType == typeCosmAny { + // Unpack field, value as Any + if fieldType, field, err = UnpackAny(cdc, field); err != nil { + return err + } + } + + // If field is an empty value, do not include in types, since it will not + // be present in the object + if field.IsZero() { + continue + } + + for { + if fieldType.Kind() == reflect.Ptr { + fieldType = fieldType.Elem() + + if field.IsValid() { + field = field.Elem() + } + + continue + } + + if fieldType.Kind() == reflect.Interface { + fieldType = reflect.TypeOf(field.Interface()) + continue + } + + if field.Kind() == reflect.Ptr { + field = field.Elem() + continue + } + + break + } + + var isCollection bool + if fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice { + if field.Len() == 0 { + // skip empty collections from type mapping + continue + } + + fieldType = fieldType.Elem() + field = field.Index(0) + isCollection = true + + if fieldType == typeCosmAny { + if fieldType, field, err = UnpackAny(cdc, field); err != nil { + return err + } + } + } + + for { + if fieldType.Kind() == reflect.Ptr { + fieldType = fieldType.Elem() + + if field.IsValid() { + field = field.Elem() + } + + continue + } + + if fieldType.Kind() == reflect.Interface { + fieldType = reflect.TypeOf(field.Interface()) + continue + } + + if field.Kind() == reflect.Ptr { + field = field.Elem() + continue + } + + break + } + + fieldPrefix := fmt.Sprintf("%s.%s", prefix, fieldName) + + ethTyp := TypToEth(fieldType) + + if len(ethTyp) > 0 { + // Support array of uint64 + if isCollection && fieldType.Kind() != reflect.Slice && fieldType.Kind() != reflect.Array { + ethTyp += "[]" + } + + if prefix == typeDefPrefix { + typeMap[rootType] = append(typeMap[rootType], apitypes.Type{ + Name: fieldName, + Type: ethTyp, + }) + } else { + typeDef := sanitizeTypedef(prefix) + typeMap[typeDef] = append(typeMap[typeDef], apitypes.Type{ + Name: fieldName, + Type: ethTyp, + }) + } + + continue + } + + if fieldType.Kind() == reflect.Struct { + var fieldTypedef string + + if isCollection { + fieldTypedef = sanitizeTypedef(fieldPrefix) + "[]" + } else { + fieldTypedef = sanitizeTypedef(fieldPrefix) + } + + if prefix == typeDefPrefix { + typeMap[rootType] = append(typeMap[rootType], apitypes.Type{ + Name: fieldName, + Type: fieldTypedef, + }) + } else { + typeDef := sanitizeTypedef(prefix) + typeMap[typeDef] = append(typeMap[typeDef], apitypes.Type{ + Name: fieldName, + Type: fieldTypedef, + }) + } + + if err := legacyTraverseFields(cdc, typeMap, rootType, fieldPrefix, fieldType, field); err != nil { + return err + } + + continue + } + } + + return nil +} + +func jsonNameFromTag(tag reflect.StructTag) string { + jsonTags := tag.Get("json") + parts := strings.Split(jsonTags, ",") + return parts[0] +} + +// Unpack the given Any value with Type/Value deconstruction +func UnpackAny(cdc codectypes.AnyUnpacker, field reflect.Value) (reflect.Type, reflect.Value, error) { + anyData, ok := field.Interface().(*codectypes.Any) + if !ok { + return nil, reflect.Value{}, errorsmod.Wrapf(errortypes.ErrPackAny, "%T", field.Interface()) + } + + anyWrapper := &CosmosAnyWrapper{ + Type: anyData.TypeUrl, + } + + if err := cdc.UnpackAny(anyData, &anyWrapper.Value); err != nil { + return nil, reflect.Value{}, errorsmod.Wrap(err, "failed to unpack Any in msg struct") + } + + fieldType := reflect.TypeOf(anyWrapper) + field = reflect.ValueOf(anyWrapper) + + return fieldType, field, nil +} + +var ( + typeEthHash = reflect.TypeOf(common.Hash{}) + typeEthAddr = reflect.TypeOf(common.Address{}) + typeBigInt = reflect.TypeOf(big.Int{}) + typeCosmInt = reflect.TypeOf(sdkmath.Int{}) + typeCosmDec = reflect.TypeOf(sdkmath.LegacyDec{}) + typeTime = reflect.TypeOf(time.Time{}) + typeCosmAny = reflect.TypeOf(&codectypes.Any{}) + typeEd25519 = reflect.TypeOf(ed25519.PubKey{}) +) + +// TypToEth supports only basic types and arrays of basic types. +// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md +func TypToEth(typ reflect.Type) string { + const str = "string" + + switch typ.Kind() { + case reflect.String: + return str + case reflect.Bool: + return "bool" + case reflect.Int: + return "int64" + case reflect.Int8: + return "int8" + case reflect.Int16: + return "int16" + case reflect.Int32: + return "int32" + case reflect.Int64: + return "int64" + case reflect.Uint: + return "uint64" + case reflect.Uint8: + return "uint8" + case reflect.Uint16: + return "uint16" + case reflect.Uint32: + return "uint32" + case reflect.Uint64: + return "uint64" + case reflect.Slice: + ethName := TypToEth(typ.Elem()) + if len(ethName) > 0 { + return ethName + "[]" + } + case reflect.Array: + ethName := TypToEth(typ.Elem()) + if len(ethName) > 0 { + return ethName + "[]" + } + case reflect.Ptr: + if typ.Elem().ConvertibleTo(typeBigInt) || + typ.Elem().ConvertibleTo(typeTime) || + typ.Elem().ConvertibleTo(typeEd25519) || + typ.Elem().ConvertibleTo(typeCosmDec) || + typ.Elem().ConvertibleTo(typeCosmInt) { + return str + } + case reflect.Struct: + if typ.ConvertibleTo(typeEthHash) || + typ.ConvertibleTo(typeEthAddr) || + typ.ConvertibleTo(typeBigInt) || + typ.ConvertibleTo(typeEd25519) || + typ.ConvertibleTo(typeTime) || + typ.ConvertibleTo(typeCosmDec) || + typ.ConvertibleTo(typeCosmInt) { + return str + } + } + + return "" +} diff --git a/eth/eip712/eip712_test.go b/eth/eip712/eip712_test.go new file mode 100644 index 000000000..ab1538c34 --- /dev/null +++ b/eth/eip712/eip712_test.go @@ -0,0 +1,722 @@ +package eip712_test + +import ( + "bytes" + "fmt" + "math/big" + "reflect" + "testing" + + sdkmath "cosmossdk.io/math" + sdkcodec "github.com/cosmos/cosmos-sdk/codec/types" + + chainparams "cosmossdk.io/simapp/params" + "github.com/cosmos/cosmos-sdk/client" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/eth/eip712" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/evm" + + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" + + "github.com/NibiruChain/nibiru/v2/app" + + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/NibiruChain/nibiru/v2/eth/encoding" + + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/suite" +) + +// Unit tests for single-signer EIP-712 signature verification. Multi-signature key verification tests are out-of-scope +// here and included with the ante_tests. + +const ( + msgsFieldName = "msgs" + TESTNET_CHAIN_ID = "nibiru_9000" +) + +type EIP712TestSuite struct { + suite.Suite + + config chainparams.EncodingConfig + clientCtx client.Context + useLegacyEIP712TypedData bool + denom string +} + +type EIP712TestParams struct { + fee sdktx.Fee + address sdk.AccAddress + accountNumber uint64 + sequence uint64 + memo string +} + +func TestEIP712TestSuite(t *testing.T) { + suite.Run(t, &EIP712TestSuite{}) + // Note that we don't test the Legacy EIP-712 Extension, since that case + // is sufficiently covered by the AnteHandler tests. + suite.Run(t, &EIP712TestSuite{ + useLegacyEIP712TypedData: true, + }) +} + +func (suite *EIP712TestSuite) SetupTest() { + suite.config = encoding.MakeConfig(app.ModuleBasics) + suite.clientCtx = client.Context{}.WithTxConfig(suite.config.TxConfig) + suite.denom = evm.EVMBankDenom + + sdk.GetConfig().SetBech32PrefixForAccount(appconst.AccountAddressPrefix, "") + eip712.SetEncodingConfig(suite.config) +} + +// createTestAddress creates random test addresses for messages +func (suite *EIP712TestSuite) createTestAddress() sdk.AccAddress { + privkey, _ := ethsecp256k1.GenerateKey() + key, err := privkey.ToECDSA() + suite.Require().NoError(err) + + addr := crypto.PubkeyToAddress(key.PublicKey) + + return addr.Bytes() +} + +// createTestKeyPair creates a random keypair for signing and verification +func (suite *EIP712TestSuite) createTestKeyPair() (*ethsecp256k1.PrivKey, *ethsecp256k1.PubKey) { + privKey, err := ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + + pubKey := ðsecp256k1.PubKey{ + Key: privKey.PubKey().Bytes(), + } + suite.Require().Implements((*cryptotypes.PubKey)(nil), pubKey) + + return privKey, pubKey +} + +// makeCoins helps create an instance of sdk.Coins[] with single coin +func (suite *EIP712TestSuite) makeCoins(denom string, amount sdkmath.Int) sdk.Coins { + return sdk.NewCoins( + sdk.NewCoin( + denom, + amount, + ), + ) +} + +func (suite *EIP712TestSuite) TestEIP712() { + suite.SetupTest() + + signModes := []signing.SignMode{ + signing.SignMode_SIGN_MODE_DIRECT, + signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + } + + params := EIP712TestParams{ + fee: sdktx.Fee{ + Amount: suite.makeCoins(suite.denom, sdkmath.NewInt(2000)), + GasLimit: 20000, + }, + address: suite.createTestAddress(), + accountNumber: 25, + sequence: 78, + memo: "", + } + + testCases := []struct { + title string + chainID string + msgs []sdk.Msg + timeoutHeight uint64 + expectSuccess bool + }{ + { + title: "Succeeds - Standard MsgSend", + msgs: []sdk.Msg{ + banktypes.NewMsgSend( + suite.createTestAddress(), + suite.createTestAddress(), + suite.makeCoins(suite.denom, sdkmath.NewInt(1)), + ), + }, + expectSuccess: true, + }, + { + title: "Succeeds - Standard MsgVote", + msgs: []sdk.Msg{ + govtypes.NewMsgVote( + suite.createTestAddress(), + 5, + govtypes.OptionNo, + ), + }, + expectSuccess: true, + }, + { + title: "Succeeds - Standard MsgDelegate", + msgs: []sdk.Msg{ + stakingtypes.NewMsgDelegate( + suite.createTestAddress(), + sdk.ValAddress(suite.createTestAddress()), + suite.makeCoins(suite.denom, sdkmath.NewInt(1))[0], + ), + }, + expectSuccess: true, + }, + { + title: "Succeeds - Standard MsgWithdrawDelegationReward", + msgs: []sdk.Msg{ + distributiontypes.NewMsgWithdrawDelegatorReward( + suite.createTestAddress(), + sdk.ValAddress(suite.createTestAddress()), + ), + }, + expectSuccess: true, + }, + { + title: "Succeeds - Two Single-Signer MsgDelegate", + msgs: []sdk.Msg{ + stakingtypes.NewMsgDelegate( + params.address, + sdk.ValAddress(suite.createTestAddress()), + suite.makeCoins(suite.denom, sdkmath.NewInt(1))[0], + ), + stakingtypes.NewMsgDelegate( + params.address, + sdk.ValAddress(suite.createTestAddress()), + suite.makeCoins(suite.denom, sdkmath.NewInt(5))[0], + ), + }, + expectSuccess: true, + }, + { + title: "Succeeds - Single-Signer MsgVote V1 with Omitted Value", + msgs: []sdk.Msg{ + govtypesv1.NewMsgVote( + params.address, + 5, + govtypesv1.VoteOption_VOTE_OPTION_NO, + "", + ), + }, + expectSuccess: true, + }, + { + title: "Succeeds - Single-Signer MsgSend + MsgVote", + msgs: []sdk.Msg{ + govtypes.NewMsgVote( + params.address, + 5, + govtypes.OptionNo, + ), + banktypes.NewMsgSend( + params.address, + suite.createTestAddress(), + suite.makeCoins(suite.denom, sdkmath.NewInt(50)), + ), + }, + expectSuccess: !suite.useLegacyEIP712TypedData, + }, + { + title: "Succeeds - Single-Signer 2x MsgVoteV1 with Different Schemas", + msgs: []sdk.Msg{ + govtypesv1.NewMsgVote( + params.address, + 5, + govtypesv1.VoteOption_VOTE_OPTION_NO, + "", + ), + govtypesv1.NewMsgVote( + params.address, + 10, + govtypesv1.VoteOption_VOTE_OPTION_YES, + "Has Metadata", + ), + }, + expectSuccess: !suite.useLegacyEIP712TypedData, + }, + { + title: "Fails - Two MsgVotes with Different Signers", + msgs: []sdk.Msg{ + govtypes.NewMsgVote( + suite.createTestAddress(), + 5, + govtypes.OptionNo, + ), + govtypes.NewMsgVote( + suite.createTestAddress(), + 25, + govtypes.OptionAbstain, + ), + }, + expectSuccess: false, + }, + { + title: "Fails - Empty Transaction", + msgs: []sdk.Msg{}, + expectSuccess: false, + }, + { + title: "Success - Invalid ChainID uses default", + chainID: "invalidchainid", + msgs: []sdk.Msg{ + govtypes.NewMsgVote( + suite.createTestAddress(), + 5, + govtypes.OptionNo, + ), + }, + expectSuccess: true, + }, + { + title: "Fails - Includes TimeoutHeight", + msgs: []sdk.Msg{ + govtypes.NewMsgVote( + suite.createTestAddress(), + 5, + govtypes.OptionNo, + ), + }, + timeoutHeight: 1000, + expectSuccess: false, + }, + { + title: "Fails - Single Message / Multi-Signer", + msgs: []sdk.Msg{ + banktypes.NewMsgMultiSend( + []banktypes.Input{ + banktypes.NewInput( + suite.createTestAddress(), + suite.makeCoins(suite.denom, sdkmath.NewInt(50)), + ), + banktypes.NewInput( + suite.createTestAddress(), + suite.makeCoins(suite.denom, sdkmath.NewInt(50)), + ), + }, + []banktypes.Output{ + banktypes.NewOutput( + suite.createTestAddress(), + suite.makeCoins(suite.denom, sdkmath.NewInt(50)), + ), + banktypes.NewOutput( + suite.createTestAddress(), + suite.makeCoins(suite.denom, sdkmath.NewInt(50)), + ), + }, + ), + }, + expectSuccess: false, + }, + } + + for _, tc := range testCases { + for _, signMode := range signModes { + suite.Run(tc.title, func() { + privKey, pubKey := suite.createTestKeyPair() + + txBuilder := suite.clientCtx.TxConfig.NewTxBuilder() + + txBuilder.SetGasLimit(params.fee.GasLimit) + txBuilder.SetFeeAmount(params.fee.Amount) + + err := txBuilder.SetMsgs(tc.msgs...) + suite.Require().NoError(err) + + txBuilder.SetMemo(params.memo) + + // Prepare signature field with empty signatures + txSigData := signing.SingleSignatureData{ + SignMode: signMode, + Signature: nil, + } + txSig := signing.SignatureV2{ + PubKey: pubKey, + Data: &txSigData, + Sequence: params.sequence, + } + + err = txBuilder.SetSignatures([]signing.SignatureV2{txSig}...) + suite.Require().NoError(err) + + chainID := TESTNET_CHAIN_ID + "-1" + if tc.chainID != "" { + chainID = tc.chainID + } + + if tc.timeoutHeight != 0 { + txBuilder.SetTimeoutHeight(tc.timeoutHeight) + } + + signerData := authsigning.SignerData{ + ChainID: chainID, + AccountNumber: params.accountNumber, + Sequence: params.sequence, + PubKey: pubKey, + Address: sdk.MustBech32ifyAddressBytes(appconst.AccountAddressPrefix, pubKey.Bytes()), + } + + bz, err := suite.clientCtx.TxConfig.SignModeHandler().GetSignBytes( + signMode, + signerData, + txBuilder.GetTx(), + ) + suite.Require().NoError(err) + + suite.verifyEIP712SignatureVerification(tc.expectSuccess, *privKey, *pubKey, bz) + + // Verify payload flattening only if the payload is in valid JSON format + if signMode == signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON { + suite.verifySignDocFlattening(bz) + + if tc.expectSuccess { + suite.verifyBasicTypedData(bz) + } + } + }) + } + } +} + +// verifyEIP712SignatureVerification verifies that the payload passes signature verification if signed as its EIP-712 representation. +func (suite *EIP712TestSuite) verifyEIP712SignatureVerification(expectedSuccess bool, privKey ethsecp256k1.PrivKey, pubKey ethsecp256k1.PubKey, signBytes []byte) { + eip712Bytes, err := eip712.GetEIP712BytesForMsg(signBytes) + + if suite.useLegacyEIP712TypedData { + eip712Bytes, err = eip712.LegacyGetEIP712BytesForMsg(signBytes) + } + + if !expectedSuccess { + suite.Require().Error(err) + return + } + + suite.Require().NoError(err) + + sig, err := privKey.Sign(eip712Bytes) + suite.Require().NoError(err) + + // Verify against original payload bytes. This should pass, even though it is not + // the original message that was signed. + res := pubKey.VerifySignature(signBytes, sig) + suite.Require().True(res) + + // Verify against the signed EIP-712 bytes. This should pass, since it is the message signed. + res = pubKey.VerifySignature(eip712Bytes, sig) + suite.Require().True(res) + + // Verify against random bytes to ensure it does not pass unexpectedly (sanity check). + randBytes := make([]byte, len(signBytes)) + copy(randBytes, signBytes) + // Change the first element of signBytes to a different value + randBytes[0] = (signBytes[0] + 10) % 255 + res = pubKey.VerifySignature(randBytes, sig) + suite.Require().False(res) +} + +// verifySignDocFlattening tests the flattening algorithm against the sign doc's JSON payload, +// using verifyPayloadAgainstFlattened. +func (suite *EIP712TestSuite) verifySignDocFlattening(signDoc []byte) { + payload := gjson.ParseBytes(signDoc) + suite.Require().True(payload.IsObject()) + + flattened, _, err := eip712.FlattenPayloadMessages(payload) + suite.Require().NoError(err) + + suite.verifyPayloadAgainstFlattened(payload, flattened) +} + +// verifyPayloadAgainstFlattened compares a payload against its flattened counterpart to ensure that +// the flattening algorithm behaved as expected. +func (suite *EIP712TestSuite) verifyPayloadAgainstFlattened(payload gjson.Result, flattened gjson.Result) { + payloadMap, ok := payload.Value().(map[string]interface{}) + suite.Require().True(ok) + flattenedMap, ok := flattened.Value().(map[string]interface{}) + suite.Require().True(ok) + + suite.verifyPayloadMapAgainstFlattenedMap(payloadMap, flattenedMap) +} + +// verifyPayloadMapAgainstFlattenedMap directly compares two JSON maps in Go representations to +// test flattening. +func (suite *EIP712TestSuite) verifyPayloadMapAgainstFlattenedMap(original map[string]interface{}, flattened map[string]interface{}) { + interfaceMessages, ok := original[msgsFieldName] + suite.Require().True(ok) + + messages, ok := interfaceMessages.([]interface{}) + suite.Require().True(ok) + + // Verify message contents + for i, msg := range messages { + flattenedMsg, ok := flattened[fmt.Sprintf("msg%d", i)] + suite.Require().True(ok) + + flattenedMsgJSON, ok := flattenedMsg.(map[string]interface{}) + suite.Require().True(ok) + + suite.Require().Equal(flattenedMsgJSON, msg) + } + + // Verify new payload does not have msgs field + _, ok = flattened[msgsFieldName] + suite.Require().False(ok) + + // Verify number of total keys + numKeysOriginal := len(original) + numKeysFlattened := len(flattened) + numMessages := len(messages) + + // + N keys, then -1 for msgs + suite.Require().Equal(numKeysFlattened, numKeysOriginal+numMessages-1) + + // Verify contents of remaining keys + for k, obj := range original { + if k == msgsFieldName { + continue + } + + flattenedObj, ok := flattened[k] + suite.Require().True(ok) + + suite.Require().Equal(obj, flattenedObj) + } +} + +// verifyBasicTypedData performs basic verification on the TypedData generation. +func (suite *EIP712TestSuite) verifyBasicTypedData(signDoc []byte) { + typedData, err := eip712.GetEIP712TypedDataForMsg(signDoc) + + suite.Require().NoError(err) + + jsonPayload := gjson.ParseBytes(signDoc) + suite.Require().True(jsonPayload.IsObject()) + + flattened, _, err := eip712.FlattenPayloadMessages(jsonPayload) + suite.Require().NoError(err) + suite.Require().True(flattened.IsObject()) + + flattenedMsgMap, ok := flattened.Value().(map[string]interface{}) + suite.Require().True(ok) + + suite.Require().Equal(typedData.Message, flattenedMsgMap) +} + +// TestFlattenPayloadErrorHandling tests error handling in TypedData generation, +// specifically regarding the payload. +func (suite *EIP712TestSuite) TestFlattenPayloadErrorHandling() { + // No msgs + _, _, err := eip712.FlattenPayloadMessages(gjson.Parse("")) + suite.Require().ErrorContains(err, "no messages found") + + // Non-array Msgs + _, _, err = eip712.FlattenPayloadMessages(gjson.Parse(`{"msgs": 10}`)) + suite.Require().ErrorContains(err, "array of messages") + + // Array with non-object items + _, _, err = eip712.FlattenPayloadMessages(gjson.Parse(`{"msgs": [10, 20]}`)) + suite.Require().ErrorContains(err, "not valid JSON") + + // Malformed payload + malformed, err := sjson.Set(suite.generateRandomPayload(2).Raw, "msg0", 20) + suite.Require().NoError(err) + _, _, err = eip712.FlattenPayloadMessages(gjson.Parse(malformed)) + suite.Require().ErrorContains(err, "malformed payload") +} + +// TestTypedDataErrorHandling tests error handling for TypedData generation +// in the main algorithm. +func (suite *EIP712TestSuite) TestTypedDataErrorHandling() { + // Empty JSON + _, err := eip712.WrapTxToTypedData(0, make([]byte, 0)) + suite.Require().ErrorContains(err, "invalid JSON") + + _, err = eip712.WrapTxToTypedData(0, []byte(gjson.Parse(`{"msgs": 10}`).Raw)) + suite.Require().ErrorContains(err, "array of messages") + + // Invalid message 'type' + _, err = eip712.WrapTxToTypedData(0, []byte(gjson.Parse(`{"msgs": [{ "type": 10 }] }`).Raw)) + suite.Require().ErrorContains(err, "message type value") + + // Max duplicate type recursion depth + messagesArr := new(bytes.Buffer) + maxRecursionDepth := 1001 + + messagesArr.WriteString("[") + for i := 0; i < maxRecursionDepth; i++ { + messagesArr.WriteString(fmt.Sprintf(`{ "type": "msgType", "value": { "field%v": 10 } }`, i)) + if i != maxRecursionDepth-1 { + messagesArr.WriteString(",") + } + } + messagesArr.WriteString("]") + + _, err = eip712.WrapTxToTypedData(0, []byte(fmt.Sprintf(`{ "msgs": %v }`, messagesArr))) + suite.Require().ErrorContains(err, "maximum number of duplicates") +} + +// TestTypedDataEdgeCases tests certain interesting edge cases to ensure that they work +// (or don't work) as expected. +func (suite *EIP712TestSuite) TestTypedDataEdgeCases() { + // Type without '/' separator + typedData, err := eip712.WrapTxToTypedData(0, []byte(gjson.Parse(`{"msgs": [{ "type": "MsgSend", "value": { "field": 10 } }] }`).Raw)) + suite.Require().NoError(err) + types := typedData.Types["TypeMsgSend0"] + suite.Require().Greater(len(types), 0) + + // Null value + typedData, err = eip712.WrapTxToTypedData(0, []byte(gjson.Parse(`{"msgs": [{ "type": "MsgSend", "value": { "field": null } }] }`).Raw)) + suite.Require().NoError(err) + types = typedData.Types["TypeValue0"] + // Skip null type, since we don't expect any in the payload + suite.Require().Equal(len(types), 0) + + // Boolean value + typedData, err = eip712.WrapTxToTypedData(0, []byte(gjson.Parse(`{"msgs": [{ "type": "MsgSend", "value": { "field": true } }] }`).Raw)) + suite.Require().NoError(err) + types = typedData.Types["TypeValue0"] + suite.Require().Equal(len(types), 1) + suite.Require().Equal(types[0], apitypes.Type{ + Name: "field", + Type: "bool", + }) + + // Empty array + typedData, err = eip712.WrapTxToTypedData(0, []byte(gjson.Parse(`{"msgs": [{ "type": "MsgSend", "value": { "field": [] } }] }`).Raw)) + suite.Require().NoError(err) + types = typedData.Types["TypeValue0"] + suite.Require().Equal(types[0], apitypes.Type{ + Name: "field", + Type: "string[]", + }) + + // Simple arrays + typedData, err = eip712.WrapTxToTypedData(0, []byte(gjson.Parse(`{"msgs": [{ "type": "MsgSend", "value": { "array": [1, 2, 3] } }] }`).Raw)) + suite.Require().NoError(err) + types = typedData.Types["TypeValue0"] + suite.Require().Equal(len(types), 1) + suite.Require().Equal(types[0], apitypes.Type{ + Name: "array", + Type: "int64[]", + }) + + // Nested arrays (EIP-712 does not support nested arrays) + typedData, err = eip712.WrapTxToTypedData(0, []byte(gjson.Parse(`{"msgs": [{ "type": "MsgSend", "value": { "array": [[1, 2, 3], [1, 2]] } }] }`).Raw)) + suite.Require().NoError(err) + types = typedData.Types["TypeValue0"] + suite.Require().Equal(len(types), 0) +} + +// TestTypedDataGeneration tests certain qualities about the output Types representation. +func (s *EIP712TestSuite) TestTypedDataGeneration() { + // Multiple messages with the same schema should share one type + payloadRaw := `{ "msgs": [{ "type": "msgType", "value": { "field1": 10 }}, { "type": "msgType", "value": { "field1": 20 }}] }` + + typedData, err := eip712.WrapTxToTypedData(0, []byte(payloadRaw)) + s.Require().NoError(err) + s.Require().True(typedData.Types["TypemsgType1"] == nil) + + // Multiple messages with different schemas should have different types + payloadRaw = `{ "msgs": [{ "type": "msgType", "value": { "field1": 10 }}, { "type": "msgType", "value": { "field2": 20 }}] }` + + typedData, err = eip712.WrapTxToTypedData(0, []byte(payloadRaw)) + s.Require().NoError(err) + s.Require().False(typedData.Types["TypemsgType1"] == nil) +} + +func (s *EIP712TestSuite) TestTypToEth() { + cases := []struct { + want string + given any + }{ + {want: "string", given: "string"}, + {want: "int8", given: int8(0)}, + {want: "int16", given: int16(0)}, + {want: "int32", given: int32(0)}, + {want: "int64", given: int64(0)}, + + {want: "uint64", given: uint(0)}, + {want: "uint8", given: uint8(0)}, + {want: "uint16", given: uint16(0)}, + {want: "uint32", given: uint32(0)}, + {want: "uint64", given: uint64(0)}, + {want: "bool", given: false}, + + // slice and array cases + {want: "uint64[]", given: []uint64{1, 2, 3}}, + {want: "string[]", given: []string{"1", "2"}}, + {want: "int8[]", given: [3]int8{3, 2, 1}}, + + // pointer cases + {want: "string", given: sdkmath.NewInt(1)}, + {want: "string", given: big.NewInt(1)}, + {want: "string", given: sdkmath.LegacyNewDec(1)}, + } + + for _, tc := range cases { + fnInp := reflect.TypeOf(tc.given) + result := eip712.TypToEth(fnInp) + s.Equal(tc.want, result, + "Type conversion did not match for %v with input %s", tc.given, fnInp) + } +} + +func (s *EIP712TestSuite) TestUnpackAny() { + _, addr := testutil.PrivKey() + cases := []struct { + wantWrappedType string + wantType string + given sdk.Msg + wantErr bool + }{ + { + wantWrappedType: "*eip712.CosmosAnyWrapper", + wantType: "/cosmos.bank.v1beta1.MsgSend", + given: banktypes.NewMsgSend(addr, addr, sdk.NewCoins(sdk.NewInt64Coin("unibi", 25))), + }, + { + wantWrappedType: "*eip712.CosmosAnyWrapper", + wantType: "/eth.evm.v1.MsgEthereumTx", + given: new(evm.MsgEthereumTx), + }, + { + given: nil, + wantErr: true, + }, + } + + for _, tc := range cases { + anyGiven, err := sdkcodec.NewAnyWithValue(tc.given) + if tc.wantErr { + s.Require().Error(err) + continue + } + s.NoError(err) + + reflectVal := reflect.ValueOf(anyGiven) + gotReflectType, gotReflectVal, err := eip712.UnpackAny(s.config.Codec, reflectVal) + s.Require().NoError(err, + "got reflect.Type %s, got reflect.Value %s", + gotReflectType, gotReflectVal) + + s.Equal(tc.wantWrappedType, gotReflectType.String()) + if gotWrappedAny := gotReflectVal.Interface().(*eip712.CosmosAnyWrapper); gotWrappedAny != nil { + s.EqualValues(gotWrappedAny.Type, tc.wantType) + } + } +} diff --git a/eth/eip712/encoding.go b/eth/eip712/encoding.go new file mode 100644 index 000000000..2950d68b1 --- /dev/null +++ b/eth/eip712/encoding.go @@ -0,0 +1,234 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eip712 + +import ( + "errors" + "fmt" + + "cosmossdk.io/simapp/params" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + + sdk "github.com/cosmos/cosmos-sdk/types" + txTypes "github.com/cosmos/cosmos-sdk/types/tx" + + apitypes "github.com/ethereum/go-ethereum/signer/core/apitypes" + + "github.com/NibiruChain/nibiru/v2/eth" + + "github.com/cosmos/cosmos-sdk/codec" +) + +var ( + protoCodec codec.ProtoCodecMarshaler + aminoCodec *codec.LegacyAmino +) + +// SetEncodingConfig set the encoding config to the singleton codecs (Amino and Protobuf). +// The process of unmarshaling SignDoc bytes into a SignDoc object requires having a codec +// populated with all relevant message types. As a result, we must call this method on app +// initialization with the app's encoding config. +func SetEncodingConfig(cfg params.EncodingConfig) { + aminoCodec = cfg.Amino + protoCodec = codec.NewProtoCodec(cfg.InterfaceRegistry) +} + +// GetEIP712BytesForMsg returns the EIP-712 object bytes for the given SignDoc bytes by decoding the bytes into +// an EIP-712 object, then converting via WrapTxToTypedData. See https://eips.ethereum.org/EIPS/eip-712 for more. +func GetEIP712BytesForMsg(signDocBytes []byte) ([]byte, error) { + typedData, err := GetEIP712TypedDataForMsg(signDocBytes) + if err != nil { + return nil, err + } + + _, rawData, err := apitypes.TypedDataAndHash(typedData) + if err != nil { + return nil, fmt.Errorf("could not get EIP-712 object bytes: %w", err) + } + + return []byte(rawData), nil +} + +// GetEIP712TypedDataForMsg returns the EIP-712 TypedData representation for either +// Amino or Protobuf encoded signature doc bytes. +func GetEIP712TypedDataForMsg(signDocBytes []byte) (apitypes.TypedData, error) { + // Attempt to decode as both Amino and Protobuf since the message format is unknown. + // If either decode works, we can move forward with the corresponding typed data. + typedDataAmino, errAmino := decodeAminoSignDoc(signDocBytes) + if errAmino == nil && isValidEIP712Payload(typedDataAmino) { + return typedDataAmino, nil + } + typedDataProtobuf, errProtobuf := decodeProtobufSignDoc(signDocBytes) + if errProtobuf == nil && isValidEIP712Payload(typedDataProtobuf) { + return typedDataProtobuf, nil + } + + return apitypes.TypedData{}, fmt.Errorf("could not decode sign doc as either Amino or Protobuf.\n amino: %v\n protobuf: %v", errAmino, errProtobuf) +} + +// isValidEIP712Payload ensures that the given TypedData does not contain empty fields from +// an improper initialization. +func isValidEIP712Payload(typedData apitypes.TypedData) bool { + return len(typedData.Message) != 0 && len(typedData.Types) != 0 && typedData.PrimaryType != "" && typedData.Domain != apitypes.TypedDataDomain{} +} + +// decodeAminoSignDoc attempts to decode the provided sign doc (bytes) as an Amino payload +// and returns a signable EIP-712 TypedData object. +func decodeAminoSignDoc(signDocBytes []byte) (apitypes.TypedData, error) { + // Ensure codecs have been initialized + if err := validateCodecInit(); err != nil { + return apitypes.TypedData{}, err + } + + var aminoDoc legacytx.StdSignDoc + if err := aminoCodec.UnmarshalJSON(signDocBytes, &aminoDoc); err != nil { + return apitypes.TypedData{}, err + } + + var fees legacytx.StdFee + if err := aminoCodec.UnmarshalJSON(aminoDoc.Fee, &fees); err != nil { + return apitypes.TypedData{}, err + } + + // Validate payload messages + msgs := make([]sdk.Msg, len(aminoDoc.Msgs)) + for i, jsonMsg := range aminoDoc.Msgs { + var m sdk.Msg + if err := aminoCodec.UnmarshalJSON(jsonMsg, &m); err != nil { + return apitypes.TypedData{}, fmt.Errorf("failed to unmarshal sign doc message: %w", err) + } + msgs[i] = m + } + + if err := validatePayloadMessages(msgs); err != nil { + return apitypes.TypedData{}, err + } + + chainID := eth.ParseEthChainID(aminoDoc.ChainID) + + typedData, err := WrapTxToTypedData( + chainID.Uint64(), + signDocBytes, + ) + if err != nil { + return apitypes.TypedData{}, fmt.Errorf("could not convert to EIP712 representation: %w", err) + } + + return typedData, nil +} + +// decodeProtobufSignDoc attempts to decode the provided sign doc (bytes) as a Protobuf payload +// and returns a signable EIP-712 TypedData object. +func decodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error) { + // Ensure codecs have been initialized + if err := validateCodecInit(); err != nil { + return apitypes.TypedData{}, err + } + + signDoc := &txTypes.SignDoc{} + if err := signDoc.Unmarshal(signDocBytes); err != nil { + return apitypes.TypedData{}, err + } + + authInfo := &txTypes.AuthInfo{} + if err := authInfo.Unmarshal(signDoc.AuthInfoBytes); err != nil { + return apitypes.TypedData{}, err + } + + body := &txTypes.TxBody{} + if err := body.Unmarshal(signDoc.BodyBytes); err != nil { + return apitypes.TypedData{}, err + } + + // Until support for these fields is added, throw an error at their presence + if body.TimeoutHeight != 0 || len(body.ExtensionOptions) != 0 || len(body.NonCriticalExtensionOptions) != 0 { + return apitypes.TypedData{}, errors.New("body contains unsupported fields: TimeoutHeight, ExtensionOptions, or NonCriticalExtensionOptions") + } + + if len(authInfo.SignerInfos) != 1 { + return apitypes.TypedData{}, fmt.Errorf("invalid number of signer infos provided, expected 1 got %v", len(authInfo.SignerInfos)) + } + + // Validate payload messages + msgs := make([]sdk.Msg, len(body.Messages)) + for i, protoMsg := range body.Messages { + var m sdk.Msg + if err := protoCodec.UnpackAny(protoMsg, &m); err != nil { + return apitypes.TypedData{}, fmt.Errorf("could not unpack message object with error %w", err) + } + msgs[i] = m + } + + if err := validatePayloadMessages(msgs); err != nil { + return apitypes.TypedData{}, err + } + + signerInfo := authInfo.SignerInfos[0] + + chainID := eth.ParseEthChainID(signDoc.ChainId) + + stdFee := &legacytx.StdFee{ + Amount: authInfo.Fee.Amount, + Gas: authInfo.Fee.GasLimit, + } + + tip := authInfo.Tip + + // WrapTxToTypedData expects the payload as an Amino Sign Doc + signBytes := legacytx.StdSignBytes( + signDoc.ChainId, + signDoc.AccountNumber, + signerInfo.Sequence, + body.TimeoutHeight, + *stdFee, + msgs, + body.Memo, + tip, + ) + + typedData, err := WrapTxToTypedData( + chainID.Uint64(), + signBytes, + ) + if err != nil { + return apitypes.TypedData{}, err + } + + return typedData, nil +} + +// validateCodecInit ensures that both Amino and Protobuf encoding codecs have been set on app init, +// so the module does not panic if either codec is not found. +func validateCodecInit() error { + if aminoCodec == nil || protoCodec == nil { + return errors.New("missing codec: codecs have not been properly initialized using SetEncodingConfig") + } + + return nil +} + +// validatePayloadMessages ensures that the transaction messages can be represented in an EIP-712 +// encoding by checking that messages exist and share a single signer. +func validatePayloadMessages(msgs []sdk.Msg) error { + if len(msgs) == 0 { + return errors.New("unable to build EIP-712 payload: transaction does contain any messages") + } + + var msgSigner sdk.AccAddress + + for i, m := range msgs { + if len(m.GetSigners()) != 1 { + return errors.New("unable to build EIP-712 payload: expect exactly 1 signer") + } + + if i == 0 { + msgSigner = m.GetSigners()[0] + continue + } + + if !msgSigner.Equals(m.GetSigners()[0]) { + return errors.New("unable to build EIP-712 payload: multiple signers detected") + } + } + + return nil +} diff --git a/eth/eip712/encoding_legacy.go b/eth/eip712/encoding_legacy.go new file mode 100644 index 000000000..23f943eaf --- /dev/null +++ b/eth/eip712/encoding_legacy.go @@ -0,0 +1,260 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eip712 + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + + sdk "github.com/cosmos/cosmos-sdk/types" + txTypes "github.com/cosmos/cosmos-sdk/types/tx" + + apitypes "github.com/ethereum/go-ethereum/signer/core/apitypes" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +type aminoMessage struct { + Type string `json:"type"` + Value any `json:"value"` +} + +// LegacyGetEIP712BytesForMsg returns the EIP-712 object bytes for the given SignDoc bytes by decoding the bytes into +// an EIP-712 object, then converting via LegacyWrapTxToTypedData. See https://eips.ethereum.org/EIPS/eip-712 for more. +func LegacyGetEIP712BytesForMsg(signDocBytes []byte) ([]byte, error) { + typedData, err := LegacyGetEIP712TypedDataForMsg(signDocBytes) + if err != nil { + return nil, err + } + + _, rawData, err := apitypes.TypedDataAndHash(typedData) + if err != nil { + return nil, fmt.Errorf("could not get EIP-712 object bytes: %w", err) + } + + return []byte(rawData), nil +} + +// LegacyGetEIP712TypedDataForMsg returns the EIP-712 TypedData representation for either +// Amino or Protobuf encoded signature doc bytes. +func LegacyGetEIP712TypedDataForMsg(signDocBytes []byte) (apitypes.TypedData, error) { + // Attempt to decode as both Amino and Protobuf since the message format is unknown. + // If either decode works, we can move forward with the corresponding typed data. + typedDataAmino, errAmino := legacyDecodeAminoSignDoc(signDocBytes) + if errAmino == nil && isValidEIP712Payload(typedDataAmino) { + return typedDataAmino, nil + } + typedDataProtobuf, errProtobuf := legacyDecodeProtobufSignDoc(signDocBytes) + if errProtobuf == nil && isValidEIP712Payload(typedDataProtobuf) { + return typedDataProtobuf, nil + } + + return apitypes.TypedData{}, fmt.Errorf("could not decode sign doc as either Amino or Protobuf.\n amino: %v\n protobuf: %v", errAmino, errProtobuf) +} + +// legacyDecodeAminoSignDoc attempts to decode the provided sign doc (bytes) as an Amino payload +// and returns a signable EIP-712 TypedData object. +func legacyDecodeAminoSignDoc(signDocBytes []byte) (apitypes.TypedData, error) { + // Ensure codecs have been initialized + if err := validateCodecInit(); err != nil { + return apitypes.TypedData{}, err + } + + var aminoDoc legacytx.StdSignDoc + if err := aminoCodec.UnmarshalJSON(signDocBytes, &aminoDoc); err != nil { + return apitypes.TypedData{}, err + } + + var fees legacytx.StdFee + if err := aminoCodec.UnmarshalJSON(aminoDoc.Fee, &fees); err != nil { + return apitypes.TypedData{}, err + } + + // Validate payload messages + msgs := make([]sdk.Msg, len(aminoDoc.Msgs)) + for i, jsonMsg := range aminoDoc.Msgs { + var m sdk.Msg + if err := aminoCodec.UnmarshalJSON(jsonMsg, &m); err != nil { + return apitypes.TypedData{}, fmt.Errorf("failed to unmarshal sign doc message: %w", err) + } + msgs[i] = m + } + + if err := legacyValidatePayloadMessages(msgs); err != nil { + return apitypes.TypedData{}, err + } + + // Use first message for fee payer and type inference + msg := msgs[0] + + // By convention, the fee payer is the first address in the list of signers. + feePayer := msg.GetSigners()[0] + feeDelegation := &FeeDelegationOptions{ + FeePayer: feePayer, + } + chainID := eth.ParseEthChainID(aminoDoc.ChainID) + + typedData, err := LegacyWrapTxToTypedData( + protoCodec, + chainID.Uint64(), + msg, + signDocBytes, + feeDelegation, + ) + if err != nil { + return apitypes.TypedData{}, fmt.Errorf("could not convert to EIP712 representation: %w", err) + } + + return typedData, nil +} + +// legacyDecodeProtobufSignDoc attempts to decode the provided sign doc (bytes) as a Protobuf payload +// and returns a signable EIP-712 TypedData object. +func legacyDecodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error) { + // Ensure codecs have been initialized + if err := validateCodecInit(); err != nil { + return apitypes.TypedData{}, err + } + + signDoc := &txTypes.SignDoc{} + if err := signDoc.Unmarshal(signDocBytes); err != nil { + return apitypes.TypedData{}, err + } + + authInfo := &txTypes.AuthInfo{} + if err := authInfo.Unmarshal(signDoc.AuthInfoBytes); err != nil { + return apitypes.TypedData{}, err + } + + body := &txTypes.TxBody{} + if err := body.Unmarshal(signDoc.BodyBytes); err != nil { + return apitypes.TypedData{}, err + } + + // Until support for these fields is added, throw an error at their presence + if body.TimeoutHeight != 0 || len(body.ExtensionOptions) != 0 || len(body.NonCriticalExtensionOptions) != 0 { + return apitypes.TypedData{}, errors.New("body contains unsupported fields: TimeoutHeight, ExtensionOptions, or NonCriticalExtensionOptions") + } + + if len(authInfo.SignerInfos) != 1 { + return apitypes.TypedData{}, fmt.Errorf("invalid number of signer infos provided, expected 1 got %v", len(authInfo.SignerInfos)) + } + + // Validate payload messages + msgs := make([]sdk.Msg, len(body.Messages)) + for i, protoMsg := range body.Messages { + var m sdk.Msg + if err := protoCodec.UnpackAny(protoMsg, &m); err != nil { + return apitypes.TypedData{}, fmt.Errorf("could not unpack message object with error %w", err) + } + msgs[i] = m + } + + if err := legacyValidatePayloadMessages(msgs); err != nil { + return apitypes.TypedData{}, err + } + + // Use first message for fee payer and type inference + msg := msgs[0] + + signerInfo := authInfo.SignerInfos[0] + + chainID := eth.ParseEthChainID(signDoc.ChainId) + + stdFee := &legacytx.StdFee{ + Amount: authInfo.Fee.Amount, + Gas: authInfo.Fee.GasLimit, + } + + feePayer := msg.GetSigners()[0] + feeDelegation := &FeeDelegationOptions{ + FeePayer: feePayer, + } + + tip := authInfo.Tip + + // WrapTxToTypedData expects the payload as an Amino Sign Doc + signBytes := legacytx.StdSignBytes( + signDoc.ChainId, + signDoc.AccountNumber, + signerInfo.Sequence, + body.TimeoutHeight, + *stdFee, + msgs, + body.Memo, + tip, + ) + + typedData, err := LegacyWrapTxToTypedData( + protoCodec, + chainID.Uint64(), + msg, + signBytes, + feeDelegation, + ) + if err != nil { + return apitypes.TypedData{}, err + } + + return typedData, nil +} + +// validatePayloadMessages ensures that the transaction messages can be represented in an EIP-712 +// encoding by checking that messages exist, are of the same type, and share a single signer. +func legacyValidatePayloadMessages(msgs []sdk.Msg) error { + if len(msgs) == 0 { + return errors.New("unable to build EIP-712 payload: transaction does contain any messages") + } + + var msgType string + var msgSigner sdk.AccAddress + + for i, m := range msgs { + t, err := getMsgType(m) + if err != nil { + return err + } + + if len(m.GetSigners()) != 1 { + return errors.New("unable to build EIP-712 payload: expect exactly 1 signer") + } + + if i == 0 { + msgType = t + msgSigner = m.GetSigners()[0] + continue + } + + if t != msgType { + return errors.New("unable to build EIP-712 payload: different types of messages detected") + } + + if !msgSigner.Equals(m.GetSigners()[0]) { + return errors.New("unable to build EIP-712 payload: multiple signers detected") + } + } + + return nil +} + +// getMsgType returns the message type prefix for the given Cosmos SDK Msg +func getMsgType(msg sdk.Msg) (string, error) { + jsonBytes, err := aminoCodec.MarshalJSON(msg) + if err != nil { + return "", err + } + + var jsonMsg aminoMessage + if err := json.Unmarshal(jsonBytes, &jsonMsg); err != nil { + return "", err + } + + // Verify Type was successfully filled in + if jsonMsg.Type == "" { + return "", errors.New("could not decode message: type is missing") + } + + return jsonMsg.Type, nil +} diff --git a/eth/eip712/message.go b/eth/eip712/message.go new file mode 100644 index 000000000..176d90b56 --- /dev/null +++ b/eth/eip712/message.go @@ -0,0 +1,148 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eip712 + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +type eip712MessagePayload struct { + payload gjson.Result + numPayloadMsgs int + message map[string]any +} + +const ( + payloadMsgsField = "msgs" +) + +// createEIP712MessagePayload generates the EIP-712 message payload +// corresponding to the input data. +func createEIP712MessagePayload(data []byte) (eip712MessagePayload, error) { + basicPayload, err := unmarshalBytesToJSONObject(data) + if err != nil { + return eip712MessagePayload{}, err + } + + payload, numPayloadMsgs, err := FlattenPayloadMessages(basicPayload) + if err != nil { + return eip712MessagePayload{}, errorsmod.Wrap(err, "failed to flatten payload JSON messages") + } + + message, ok := payload.Value().(map[string]any) + if !ok { + return eip712MessagePayload{}, errorsmod.Wrap(errortypes.ErrInvalidType, "failed to parse JSON as map") + } + + messagePayload := eip712MessagePayload{ + payload: payload, + numPayloadMsgs: numPayloadMsgs, + message: message, + } + + return messagePayload, nil +} + +// unmarshalBytesToJSONObject converts a bytestream into +// a JSON object, then makes sure the JSON is an object. +func unmarshalBytesToJSONObject(data []byte) (gjson.Result, error) { + if !gjson.ValidBytes(data) { + return gjson.Result{}, errorsmod.Wrap(errortypes.ErrJSONUnmarshal, "invalid JSON received") + } + + payload := gjson.ParseBytes(data) + + if !payload.IsObject() { + return gjson.Result{}, errorsmod.Wrap(errortypes.ErrJSONUnmarshal, "failed to JSON unmarshal data as object") + } + + return payload, nil +} + +// FlattenPayloadMessages flattens the input payload's messages, representing +// them as key-value pairs of "msg{i}": {Msg}, rather than as an array of Msgs. +// We do this to support messages with different schemas. +func FlattenPayloadMessages(payload gjson.Result) (gjson.Result, int, error) { + flattened := payload + var err error + + msgs, err := getPayloadMessages(payload) + if err != nil { + return gjson.Result{}, 0, err + } + + for i, msg := range msgs { + flattened, err = payloadWithNewMessage(flattened, msg, i) + if err != nil { + return gjson.Result{}, 0, err + } + } + + flattened, err = payloadWithoutMsgsField(flattened) + if err != nil { + return gjson.Result{}, 0, err + } + + return flattened, len(msgs), nil +} + +// getPayloadMessages processes and returns the payload messages as a JSON array. +func getPayloadMessages(payload gjson.Result) ([]gjson.Result, error) { + rawMsgs := payload.Get(payloadMsgsField) + + if !rawMsgs.Exists() { + return nil, errorsmod.Wrap(errortypes.ErrInvalidRequest, "no messages found in payload, unable to parse") + } + + if !rawMsgs.IsArray() { + return nil, errorsmod.Wrap(errortypes.ErrInvalidRequest, "expected type array of messages, cannot parse") + } + + return rawMsgs.Array(), nil +} + +// payloadWithNewMessage returns the updated payload object with the message +// set at the field corresponding to index. +func payloadWithNewMessage(payload gjson.Result, msg gjson.Result, index int) (gjson.Result, error) { + field := msgFieldForIndex(index) + + if payload.Get(field).Exists() { + return gjson.Result{}, errorsmod.Wrapf( + errortypes.ErrInvalidRequest, + "malformed payload received, did not expect to find key at field %v", field, + ) + } + + if !msg.IsObject() { + return gjson.Result{}, errorsmod.Wrapf(errortypes.ErrInvalidRequest, "msg at index %d is not valid JSON: %v", index, msg) + } + + newRaw, err := sjson.SetRaw(payload.Raw, field, msg.Raw) + if err != nil { + return gjson.Result{}, err + } + + return gjson.Parse(newRaw), nil +} + +// msgFieldForIndex returns the payload field for a given message post-flattening. +// e.g. msgs[2] becomes 'msg2' +func msgFieldForIndex(i int) string { + return fmt.Sprintf("msg%d", i) +} + +// payloadWithoutMsgsField returns the updated payload without the "msgs" array +// field, which flattening makes obsolete. +func payloadWithoutMsgsField(payload gjson.Result) (gjson.Result, error) { + newRaw, err := sjson.Delete(payload.Raw, payloadMsgsField) + if err != nil { + return gjson.Result{}, err + } + + return gjson.Parse(newRaw), nil +} diff --git a/eth/eip712/types.go b/eth/eip712/types.go new file mode 100644 index 000000000..82c4dee9f --- /dev/null +++ b/eth/eip712/types.go @@ -0,0 +1,390 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eip712 + +import ( + "bytes" + "fmt" + "sort" + "strings" + + "golang.org/x/text/cases" + "golang.org/x/text/language" + + errorsmod "cosmossdk.io/errors" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/tidwall/gjson" +) + +const ( + rootPrefix = "_" + typePrefix = "Type" + + txField = "Tx" + ethBool = "bool" + ethInt64 = "int64" + ethString = "string" + + msgTypeField = "type" + + maxDuplicateTypeDefs = 1000 +) + +// getEIP712Types creates and returns the EIP-712 types +// for the given message payload. +func createEIP712Types(messagePayload eip712MessagePayload) (apitypes.Types, error) { + eip712Types := apitypes.Types{ + "EIP712Domain": { + { + Name: "name", + Type: "string", + }, + { + Name: "version", + Type: "string", + }, + { + Name: "chainId", + Type: "uint256", + }, + { + Name: "verifyingContract", + Type: "string", + }, + { + Name: "salt", + Type: "string", + }, + }, + "Tx": { + {Name: "account_number", Type: "string"}, + {Name: "chain_id", Type: "string"}, + {Name: "fee", Type: "Fee"}, + {Name: "memo", Type: "string"}, + {Name: "sequence", Type: "string"}, + // Note timeout_height was removed because it was not getting filled with the legacyTx + }, + "Fee": { + {Name: "amount", Type: "Coin[]"}, + {Name: "gas", Type: "string"}, + }, + "Coin": { + {Name: "denom", Type: "string"}, + {Name: "amount", Type: "string"}, + }, + } + + for i := 0; i < messagePayload.numPayloadMsgs; i++ { + field := msgFieldForIndex(i) + msg := messagePayload.payload.Get(field) + + if err := addMsgTypesToRoot(eip712Types, field, msg); err != nil { + return nil, err + } + } + + return eip712Types, nil +} + +// addMsgTypesToRoot adds all types for the given message +// to eip712Types, recursively handling object sub-fields. +func addMsgTypesToRoot(eip712Types apitypes.Types, msgField string, msg gjson.Result) (err error) { + defer doRecover(&err) + + if !msg.IsObject() { + return errorsmod.Wrapf(errortypes.ErrInvalidRequest, "message is not valid JSON, cannot parse types") + } + + msgRootType, err := msgRootType(msg) + if err != nil { + return err + } + + msgTypeDef, err := recursivelyAddTypesToRoot(eip712Types, msgRootType, rootPrefix, msg) + if err != nil { + return err + } + + addMsgTypeDefToTxSchema(eip712Types, msgField, msgTypeDef) + + return nil +} + +// msgRootType parses the message and returns the formatted +// type signature corresponding to the message type. +func msgRootType(msg gjson.Result) (string, error) { + msgType := msg.Get(msgTypeField).Str + if msgType == "" { + // .Str is empty for arrays and objects + return "", errorsmod.Wrap(errortypes.ErrInvalidType, "malformed message type value, expected type string") + } + + // Convert e.g. cosmos-sdk/MsgSend to TypeMsgSend + typeTokenized := strings.Split(msgType, "/") + msgSignature := typeTokenized[len(typeTokenized)-1] + rootType := fmt.Sprintf("%v%v", typePrefix, msgSignature) + + return rootType, nil +} + +// addMsgTypeDefToTxSchema adds the message's field-type pairing +// to the Tx schema. +func addMsgTypeDefToTxSchema(eip712Types apitypes.Types, msgField, msgTypeDef string) { + eip712Types[txField] = append(eip712Types[txField], apitypes.Type{ + Name: msgField, + Type: msgTypeDef, + }) +} + +// recursivelyAddTypesToRoot walks all types in the given map +// and recursively adds sub-maps as new types when necessary. +// It adds all type definitions to typeMap, then returns a key +// to the json object's type definition within the map. +func recursivelyAddTypesToRoot( + typeMap apitypes.Types, + rootType string, + prefix string, + payload gjson.Result, +) (string, error) { + typesToAdd := []apitypes.Type{} + + // Must sort the JSON keys for deterministic type generation. + sortedFieldNames, err := sortedJSONKeys(payload) + if err != nil { + return "", errorsmod.Wrap(err, "unable to sort object keys") + } + + typeDef := typeDefForPrefix(prefix, rootType) + + for _, fieldName := range sortedFieldNames { + field := payload.Get(fieldName) + if !field.Exists() { + continue + } + + // Handle array type by unwrapping the first element. + // Note that arrays with multiple types are not supported + // using EIP-712, so we can ignore that case. + isCollection := false + if field.IsArray() { + fieldAsArray := field.Array() + + if len(fieldAsArray) == 0 { + // Arbitrarily add string[] type to handle empty arrays, + // since we cannot access the underlying object. + emptyArrayType := "string[]" + typesToAdd = appendedTypesList(typesToAdd, fieldName, emptyArrayType) + + continue + } + + field = fieldAsArray[0] + isCollection = true + } + + ethType := getEthTypeForJSON(field) + + // Handle JSON primitive types by adding the corresponding + // EIP-712 type to the types schema. + if ethType != "" { + if isCollection { + ethType += "[]" + } + typesToAdd = appendedTypesList(typesToAdd, fieldName, ethType) + + continue + } + + // Handle object types recursively. Note that nested array types are not supported + // in EIP-712, so we can exclude that case. + if field.IsObject() { + fieldPrefix := prefixForSubField(prefix, fieldName) + + fieldTypeDef, err := recursivelyAddTypesToRoot(typeMap, rootType, fieldPrefix, field) + if err != nil { + return "", err + } + + fieldTypeDef = sanitizeTypedef(fieldTypeDef) + if isCollection { + fieldTypeDef += "[]" + } + + typesToAdd = appendedTypesList(typesToAdd, fieldName, fieldTypeDef) + + continue + } + } + + return addTypesToRoot(typeMap, typeDef, typesToAdd) +} + +// sortedJSONKeys returns the sorted JSON keys for the input object, +// to be used for deterministic iteration. +func sortedJSONKeys(json gjson.Result) ([]string, error) { + if !json.IsObject() { + return nil, errorsmod.Wrap(errortypes.ErrInvalidType, "expected JSON map to parse") + } + + jsonMap := json.Map() + + keys := make([]string, len(jsonMap)) + i := 0 + // #nosec G705 for map iteration + for k := range jsonMap { + keys[i] = k + i++ + } + + sort.Slice(keys, func(i, j int) bool { + return strings.Compare(keys[i], keys[j]) > 0 + }) + + return keys, nil +} + +// typeDefForPrefix computes the type definition for the given +// prefix. This value will represent the types key within +// the EIP-712 types map. +func typeDefForPrefix(prefix, rootType string) string { + if prefix == rootPrefix { + return rootType + } + return sanitizeTypedef(prefix) +} + +// appendedTypesList returns an array of Types with a new element +// consisting of name and typeDef. +func appendedTypesList(types []apitypes.Type, name, typeDef string) []apitypes.Type { + return append(types, apitypes.Type{ + Name: name, + Type: typeDef, + }) +} + +// prefixForSubField computes the prefix for a subfield by +// indicating that it's derived from the object associated with prefix. +func prefixForSubField(prefix, fieldName string) string { + return fmt.Sprintf("%s.%s", prefix, fieldName) +} + +// addTypesToRoot attempts to add the types to the root at key +// typeDef and returns the key at which the types are present, +// or an error if they cannot be added. If the typeDef key is a +// duplicate, we return the key corresponding to an identical copy +// if present, without modifying the structure. Otherwise, we insert +// the types at the next available typeDef-{n} field. We do this to +// support identically named payloads with different schemas. +func addTypesToRoot(typeMap apitypes.Types, typeDef string, types []apitypes.Type) (string, error) { + var indexedTypeDef string + + indexAsDuplicate := 0 + + for { + indexedTypeDef = typeDefWithIndex(typeDef, indexAsDuplicate) + existingTypes, foundElement := typeMap[indexedTypeDef] + + // Found identical duplicate, so we can simply return + // the existing type definition. + if foundElement && typesAreEqual(types, existingTypes) { + return indexedTypeDef, nil + } + + // Found no element, so we can create a new one at this index. + if !foundElement { + break + } + + indexAsDuplicate++ + + if indexAsDuplicate == maxDuplicateTypeDefs { + return "", errorsmod.Wrap(errortypes.ErrInvalidRequest, "exceeded maximum number of duplicates for a single type definition") + } + } + + typeMap[indexedTypeDef] = types + + return indexedTypeDef, nil +} + +// typeDefWithIndex creates a duplicate-indexed type definition +// to differentiate between different schemas with the same name. +func typeDefWithIndex(typeDef string, index int) string { + return fmt.Sprintf("%v%d", typeDef, index) +} + +// typesAreEqual compares two apitypes.Type arrays +// and returns a boolean indicating whether they have +// the same values. +// It assumes both arrays are in the same sorted order. +func typesAreEqual(types1 []apitypes.Type, types2 []apitypes.Type) bool { + if len(types1) != len(types2) { + return false + } + + for i := 0; i < len(types1); i++ { + if types1[i].Name != types2[i].Name || types1[i].Type != types2[i].Type { + return false + } + } + + return true +} + +// _.foo_bar.baz -> TypeFooBarBaz +// +// Since Geth does not tolerate complex EIP-712 type names, we need to sanitize +// the inputs. +func sanitizeTypedef(str string) string { + buf := new(bytes.Buffer) + caser := cases.Title(language.English, cases.NoLower) + parts := strings.Split(str, ".") + + for _, part := range parts { + if part == rootPrefix { + buf.WriteString(typePrefix) + continue + } + + subparts := strings.Split(part, "_") + for _, subpart := range subparts { + buf.WriteString(caser.String(subpart)) + } + } + + return buf.String() +} + +// getEthTypeForJSON converts a JSON type to an Ethereum type. +// It returns an empty string for Objects, Arrays, or Null. +// See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md for more. +func getEthTypeForJSON(json gjson.Result) string { + switch json.Type { + case gjson.True, gjson.False: + return ethBool + case gjson.Number: + return ethInt64 + case gjson.String: + return ethString + case gjson.JSON: + // Array or Object type + return "" + default: + return "" + } +} + +// doRecover attempts to recover in the event of a panic to +// prevent DOS and gracefully handle an error instead. +func doRecover(err *error) { + if r := recover(); r != nil { + if e, ok := r.(error); ok { + e = errorsmod.Wrap(e, "panicked with error") + *err = e + return + } + + *err = fmt.Errorf("%v", r) + } +} diff --git a/eth/encoding/codec/codec.go b/eth/encoding/codec/codec.go new file mode 100644 index 000000000..702274696 --- /dev/null +++ b/eth/encoding/codec/codec.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package codec + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/std" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth" + cryptocodec "github.com/NibiruChain/nibiru/v2/eth/crypto/codec" +) + +// RegisterLegacyAminoCodec registers Interfaces from types, crypto, and SDK std. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + sdk.RegisterLegacyAminoCodec(cdc) + cryptocodec.RegisterCrypto(cdc) + codec.RegisterEvidences(cdc) +} + +// RegisterInterfaces registers Interfaces from types, crypto, and SDK std. +func RegisterInterfaces(interfaceRegistry codectypes.InterfaceRegistry) { + std.RegisterInterfaces(interfaceRegistry) + cryptocodec.RegisterInterfaces(interfaceRegistry) + eth.RegisterInterfaces(interfaceRegistry) +} diff --git a/eth/encoding/config.go b/eth/encoding/config.go new file mode 100644 index 000000000..72d03ae05 --- /dev/null +++ b/eth/encoding/config.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package encoding + +import ( + "cosmossdk.io/simapp/params" + amino "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + + enccodec "github.com/NibiruChain/nibiru/v2/eth/encoding/codec" +) + +// MakeConfig creates an EncodingConfig for testing +func MakeConfig(mb module.BasicManager) params.EncodingConfig { + cdc := amino.NewLegacyAmino() + interfaceRegistry := types.NewInterfaceRegistry() + codec := amino.NewProtoCodec(interfaceRegistry) + + encodingConfig := params.EncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Codec: codec, + TxConfig: tx.NewTxConfig(codec, tx.DefaultSignModes), + Amino: cdc, + } + + enccodec.RegisterLegacyAminoCodec(encodingConfig.Amino) + enccodec.RegisterInterfaces(encodingConfig.InterfaceRegistry) + mb.RegisterLegacyAminoCodec(encodingConfig.Amino) + mb.RegisterInterfaces(encodingConfig.InterfaceRegistry) + return encodingConfig +} diff --git a/eth/encoding/config_test.go b/eth/encoding/config_test.go new file mode 100644 index 000000000..5de6c822e --- /dev/null +++ b/eth/encoding/config_test.go @@ -0,0 +1,42 @@ +package encoding_test + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/require" + + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/eth/encoding" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func TestTxEncoding(t *testing.T) { + ethAcc := evmtest.NewEthPrivAcc() + addr, key := ethAcc.EthAddr, ethAcc.PrivKey + signer := evmtest.NewSigner(key) + + ethTxParams := evm.EvmTxArgs{ + ChainID: big.NewInt(1), + Nonce: 1, + Amount: big.NewInt(10), + GasLimit: 100000, + GasFeeCap: big.NewInt(1), + GasTipCap: big.NewInt(1), + Input: []byte{}, + } + msg := evm.NewTx(ðTxParams) + msg.From = addr.Hex() + + ethSigner := gethcore.LatestSignerForChainID(big.NewInt(1)) + err := msg.Sign(ethSigner, signer) + require.NoError(t, err) + + cfg := encoding.MakeConfig(app.ModuleBasics) + + _, err = cfg.TxConfig.TxEncoder()(msg) + require.Error(t, err, "encoding failed") +} diff --git a/eth/errors.go b/eth/errors.go new file mode 100644 index 000000000..8f2ea84f4 --- /dev/null +++ b/eth/errors.go @@ -0,0 +1,17 @@ +package eth + +import ( + sdkerrors "cosmossdk.io/errors" +) + +var moduleErrorCodeIdx uint32 = 1 + +func registerError(msg string) *sdkerrors.Error { + moduleErrorCodeIdx += 1 + return sdkerrors.Register("eth", moduleErrorCodeIdx, msg) +} + +// Module "sentinel" errors +var ( + ErrInvalidChainID = registerError("invalid Ethereum chain ID") +) diff --git a/eth/eth_account.go b/eth/eth_account.go new file mode 100644 index 000000000..85a857b5a --- /dev/null +++ b/eth/eth_account.go @@ -0,0 +1,93 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eth + +import ( + "bytes" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func EthAddrToNibiruAddr(ethAddr gethcommon.Address) sdk.AccAddress { + return ethAddr.Bytes() +} + +func NibiruAddrToEthAddr(nibiruAddr sdk.AccAddress) gethcommon.Address { + return gethcommon.BytesToAddress(nibiruAddr.Bytes()) +} + +var ( + _ authtypes.AccountI = (*EthAccount)(nil) + _ EthAccountI = (*EthAccount)(nil) + _ authtypes.GenesisAccount = (*EthAccount)(nil) + _ codectypes.UnpackInterfacesMessage = (*EthAccount)(nil) +) + +// EthAccType: Enum for Ethereum account types. +type EthAccType = int8 + +const ( + // EthAccType_EOA: For externally owned accounts (EOAs) + EthAccType_EOA EthAccType = iota + 1 + // EthAccType_Contract: For smart contracts accounts. + EthAccType_Contract +) + +// EthAccountI represents the interface of an EVM compatible account +type EthAccountI interface { //revive:disable-line:exported + authtypes.AccountI + // EthAddress returns the ethereum Address representation of the AccAddress + EthAddress() gethcommon.Address + // CodeHash is the keccak256 hash of the contract code (if any) + GetCodeHash() gethcommon.Hash + // SetCodeHash sets the code hash to the account fields + SetCodeHash(code gethcommon.Hash) error + // Type returns the type of Ethereum Account (EOA or Contract) + Type() EthAccType +} + +func (acc EthAccount) GetBaseAccount() *authtypes.BaseAccount { + return acc.BaseAccount +} + +// EthAddress returns the account address ethereum format. +func (acc EthAccount) EthAddress() gethcommon.Address { + return gethcommon.BytesToAddress(acc.GetAddress().Bytes()) +} + +func (acc EthAccount) GetCodeHash() gethcommon.Hash { + return gethcommon.HexToHash(acc.CodeHash) +} + +func (acc *EthAccount) SetCodeHash(codeHash gethcommon.Hash) error { + acc.CodeHash = codeHash.Hex() + return nil +} + +// Type returns the type of Ethereum Account (EOA or Contract) +func (acc EthAccount) Type() EthAccType { + if bytes.Equal( + emptyCodeHash, gethcommon.HexToHash(acc.CodeHash).Bytes(), + ) { + return EthAccType_EOA + } + return EthAccType_Contract +} + +var emptyCodeHash = crypto.Keccak256(nil) + +// ProtoBaseAccount: Implementation of `BaseAccount` for the `AccountI` interface +// used in the AccountKeeper from the Auth Module. [ProtoBaseAccount] is a +// drop-in replacement for the `auth.ProtoBaseAccount` from +// "cosmos-sdk/auth/types" extended to fit the the `EthAccountI` interface for +// Ethereum accounts. +func ProtoBaseAccount() authtypes.AccountI { + return &EthAccount{ + BaseAccount: &authtypes.BaseAccount{}, + CodeHash: gethcommon.BytesToHash(emptyCodeHash).String(), + } +} diff --git a/eth/eth_account_test.go b/eth/eth_account_test.go new file mode 100644 index 000000000..d33cf8fc4 --- /dev/null +++ b/eth/eth_account_test.go @@ -0,0 +1,40 @@ +package eth_test + +import ( + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *Suite) TestEthAddrToNibiruAddr() { + accInfo := evmtest.NewEthPrivAcc() + s.Equal( + accInfo.EthAddr, + eth.NibiruAddrToEthAddr(accInfo.NibiruAddr), + ) + s.Equal( + accInfo.NibiruAddr, + eth.EthAddrToNibiruAddr(accInfo.EthAddr), + ) + + s.T().Log("unit operation - hex -> nibi -> hex") + { + addr := evmtest.NewEthPrivAcc().NibiruAddr + s.Equal( + addr, + eth.EthAddrToNibiruAddr( + eth.NibiruAddrToEthAddr(addr), + ), + ) + } + + s.T().Log("unit operation - nibi -> hex -> nibi") + { + addr := evmtest.NewEthPrivAcc().EthAddr + s.Equal( + addr, + eth.NibiruAddrToEthAddr( + eth.EthAddrToNibiruAddr(addr), + ), + ) + } +} diff --git a/eth/gas_limit.go b/eth/gas_limit.go new file mode 100644 index 000000000..1606bd693 --- /dev/null +++ b/eth/gas_limit.go @@ -0,0 +1,161 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eth + +import ( + "encoding/json" + fmt "fmt" + math "math" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// BlockGasLimit: Gas (limit) as defined by the block gas meter. Gas limit is +// derived from the consensus params if the block gas meter is nil. +func BlockGasLimit(ctx sdk.Context) (gasLimit uint64) { + blockGasMeter := ctx.BlockGasMeter() + + // Get the limit from the gas meter only if its not null and not an + // InfiniteGasMeter + if blockGasMeter != nil && blockGasMeter.Limit() != 0 { + return blockGasMeter.Limit() + } + + // Otherwise get from the consensus parameters + cp := ctx.ConsensusParams() + if cp == nil || cp.Block == nil { + return 0 + } + + maxGas := cp.Block.MaxGas + + // Setting max_gas to -1 in Tendermint means there is no limit on the maximum gas consumption for transactions + // https://github.com/cometbft/cometbft/blob/v0.37.2/proto/tendermint/types/params.proto#L25-L27 + if maxGas == -1 { + return math.MaxUint64 + } + + if maxGas > 0 { + return uint64(maxGas) // #nosec G701 -- maxGas is int64 type. It can never be greater than math.MaxUint64 + } + + return 0 +} + +// NewInfiniteGasMeterWithLimit returns a reference to a new infiniteGasMeter. +func NewInfiniteGasMeterWithLimit(limit sdk.Gas) sdk.GasMeter { + return &InfiniteGasMeter{ + consumed: 0, + limit: limit, + } +} + +// NewInfiniteGasMeter: Alias for an infinite gas meter +// ([NewInfiniteGasMeterWithLimitla)] with a tracked but unenforced gas limit. +func NewInfiniteGasMeter() sdk.GasMeter { + return NewInfiniteGasMeterWithLimit(math.MaxUint64) +} + +var _ sdk.GasMeter = &InfiniteGasMeter{} + +// InfiniteGasMeter: A special impl of `sdk.GasMeter` that ignores any gas +// limits, allowing an unlimited amount of gas to be consumed. This is especially +// useful for scenarios where gas consumption needs to be monitored but not +// restricted, such as during testing or in parts of the chain where constraints +// are meant to be set differently. +type InfiniteGasMeter struct { + // consumed: Tracks the amount of gas units consumed. + consumed sdk.Gas + // limit: Nominal unit for the gas limit, which is not enforced in a way that + // restricts consumption. + limit sdk.Gas +} + +// GasConsumedToLimit returns the gas limit if gas consumed is past the limit, +// otherwise it returns the consumed gas. +// +// Note that This function is used when recovering +// from a panic in "BlockGasMeter" when the consumed gas passes the limit. +func (g *InfiniteGasMeter) GasConsumedToLimit() sdk.Gas { + return g.consumed +} + +// GasConsumed returns the gas consumed from the GasMeter. +func (g *InfiniteGasMeter) GasConsumed() sdk.Gas { + return g.consumed +} + +// Limit returns the gas limit of the GasMeter. +func (g *InfiniteGasMeter) Limit() sdk.Gas { + return g.limit +} + +// addUint64Overflow performs the addition operation on two uint64 integers and +// returns a boolean on whether or not the result overflows. +func addUint64Overflow(a, b uint64) (uint64, bool) { + if math.MaxUint64-a < b { + return 0, true + } + + return a + b, false +} + +// ConsumeGas adds the given amount of gas to the gas consumed and panics if it overflows the limit or out of gas. +func (g *InfiniteGasMeter) ConsumeGas(amount sdk.Gas, descriptor string) { + var overflow bool + // TODO: Should we set the consumed field after overflow checking? + g.consumed, overflow = addUint64Overflow(g.consumed, amount) + if overflow { + panic(ErrorGasOverflow{descriptor}) + } +} + +// RefundGas will deduct the given amount from the gas consumed. If the amount is +// greater than the gas consumed, the function will panic. +// +// Use case: This functionality enables refunding gas to the trasaction or block gas pools so that +// EVM-compatible chains can fully support the go-ethereum StateDb interface. +// See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference. +func (g *InfiniteGasMeter) RefundGas(amount sdk.Gas, descriptor string) { + if g.consumed < amount { + panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) + } + + g.consumed -= amount +} + +// IsPastLimit returns true if gas consumed is past limit, otherwise it returns +// false. In the case of the the [InfiniteGasMeter], this always returns false. +func (g *InfiniteGasMeter) IsPastLimit() bool { + return false +} + +// IsOutOfGas returns true if gas consumed is greater than or equal to gas limit, +// otherwise it returns false. In the case of the the [InfiniteGasMeter], this +// always returns false for unrestricted gas consumption. +func (g *InfiniteGasMeter) IsOutOfGas() bool { + return false +} + +// String returns the BasicGasMeter's gas limit and gas consumed. +func (g *InfiniteGasMeter) String() string { + data := map[string]uint64{"consumed": g.consumed, "limit": g.limit} + jsonData, _ := json.Marshal(data) + return fmt.Sprintf("InfiniteGasMeter: %s", jsonData) +} + +// GasRemaining returns MaxUint64 since limit is not confined in infiniteGasMeter. +func (g *InfiniteGasMeter) GasRemaining() sdk.Gas { + return math.MaxUint64 +} + +// ErrorNegativeGasConsumed defines an error thrown when the amount of gas +// refunded results in a negative gas consumed amount. +type ErrorNegativeGasConsumed struct { + Descriptor string +} + +// ErrorGasOverflow defines an error thrown when an action results gas consumption +// unsigned integer overflow. +type ErrorGasOverflow struct { + Descriptor string +} diff --git a/eth/gas_limit_test.go b/eth/gas_limit_test.go new file mode 100644 index 000000000..5e0f68ccb --- /dev/null +++ b/eth/gas_limit_test.go @@ -0,0 +1,165 @@ +package eth_test + +import ( + "math" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" +) + +// TestGasMeter: Ensures correct behvaior of the `InfiniteGasMeterWithLimit` +// implementation by checking that gas consumption and usage values are correctly +// tracked, that consuming gas within the limit does not cause a panic, and that +// gas refunds work as expected. +func (s *Suite) TestGasMeter() { + meter := eth.NewInfiniteGasMeter() + s.Require().Equal(uint64(math.MaxUint64), meter.Limit()) + s.Require().Equal(uint64(math.MaxUint64), meter.GasRemaining()) + s.Require().Equal(uint64(0), meter.GasConsumed()) + s.Require().Equal(uint64(0), meter.GasConsumedToLimit()) + + meter.ConsumeGas(10, "consume 10") + s.Require().Equal(uint64(math.MaxUint64), meter.GasRemaining()) + s.Require().Equal(uint64(10), meter.GasConsumed()) + s.Require().Equal(uint64(10), meter.GasConsumedToLimit()) + + // Test RefundGas + meter.RefundGas(1, "refund 1") + s.Require().Equal(uint64(math.MaxUint64), meter.GasRemaining()) + s.Require().Equal(uint64(9), meter.GasConsumed()) + + // Test IsPastLimit and IsOutOfGas + s.False(meter.IsPastLimit()) + s.False(meter.IsOutOfGas()) + + // Consume large amount fo gas to test overflow handling + meter.ConsumeGas(sdk.Gas(math.MaxUint64/2), "consume half max uint64") + s.Require().Panics(func() { meter.ConsumeGas(sdk.Gas(math.MaxUint64/2)+2, "panic") }) + s.Require().Panics(func() { meter.RefundGas(meter.GasConsumed()+1, "refund greater than consumed") }) + + // Additional tests for RefundGas + s.Require().NotPanics(func() { + meter.RefundGas(meter.GasConsumed(), "refund all") + }) + s.Require().Equal(uint64(0), meter.GasConsumed()) + s.Require().Panics(func() { + meter.RefundGas(meter.GasConsumed()+1, "refund more than consumed") + }) + s.Require().NotPanics(func() { meter.RefundGas(meter.GasConsumed(), "refund all consumed gas") }) + s.Require().Equal(uint64(0), meter.GasConsumed()) + s.Require().Equal(uint64(math.MaxUint64), meter.GasRemaining()) + + // Additional tests for IsPastLimit and IsOutOfGas with high gas usage + s.Equal(uint64(math.MaxUint64), meter.GasRemaining()) + meter.ConsumeGas(sdk.Gas(math.MaxUint64-1), "consume nearly all gas") + s.Equal(uint64(math.MaxUint64), meter.GasRemaining()) + s.Require().False(meter.IsPastLimit()) + s.Require().False(meter.IsOutOfGas()) + + // Test the String method + expectedString := `InfiniteGasMeter: {"consumed":18446744073709551614,"limit":18446744073709551615}` + s.Require().Equal(expectedString, meter.String()) + + // Test another instance with a specific limit + meter2 := eth.NewInfiniteGasMeterWithLimit(100) + s.Require().Equal(uint64(100), meter2.Limit()) + s.Require().Equal(uint64(0), meter2.GasConsumed()) + + meter2.ConsumeGas(50, "consume 50") + s.Require().Equal(uint64(50), meter2.GasConsumed()) + s.False(meter2.IsPastLimit()) + s.False(meter2.IsOutOfGas()) + + meter2.ConsumeGas(50, "consume remaining 50") + s.Require().Equal(uint64(math.MaxUint64), meter2.GasRemaining()) + s.False(meter2.IsPastLimit()) + s.False(meter2.IsOutOfGas()) + s.Require().NotPanics(func() { meter2.ConsumeGas(1, "exceed limit") }) + + // Test the String method for the second meter + expectedString2 := `InfiniteGasMeter: {"consumed":101,"limit":100}` + s.Require().Equal(expectedString2, meter2.String()) +} + +func (s *Suite) TestBlockGasLimit() { + newCtx := func() sdk.Context { return evmtest.NewTestDeps().Ctx } + tests := []struct { + name string + setupContext func() sdk.Context + wantGasLimit uint64 + }{ + { + name: "BlockGasMeter is not nil and has a non-zero limit", + setupContext: func() sdk.Context { + ctx := newCtx() + gasMeter := eth.NewInfiniteGasMeterWithLimit(100) + ctx = ctx.WithBlockGasMeter(gasMeter) + return ctx + }, + wantGasLimit: 100, + }, + { + name: "BlockGasMeter is nil and ConsensusParams is nil", + setupContext: func() sdk.Context { + ctx := newCtx() + ctx = ctx.WithConsensusParams(nil) + return ctx + }, + wantGasLimit: 0, + }, + { + name: "BlockGasMeter is nil and ConsensusParams has Block with MaxGas -1", + setupContext: func() sdk.Context { + ctx := newCtx() + cp := &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{ + MaxGas: -1, + }, + } + ctx = ctx.WithConsensusParams(cp) + return ctx + }, + wantGasLimit: math.MaxUint64, + }, + { + name: "BlockGasMeter is nil and ConsensusParams has Block with MaxGas > 0", + setupContext: func() sdk.Context { + ctx := newCtx() + cp := &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{ + MaxGas: 1000, + }, + } + ctx = ctx.WithConsensusParams(cp) + return ctx + }, + wantGasLimit: 1000, + }, + { + name: "BlockGasMeter is nil and ConsensusParams has Block with MaxGas 0", + setupContext: func() sdk.Context { + ctx := newCtx() + cp := &tmproto.ConsensusParams{ + Block: &tmproto.BlockParams{ + MaxGas: 0, + }, + } + ctx = ctx.WithConsensusParams(cp) + return ctx + }, + wantGasLimit: 0, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + ctx := tt.setupContext() + gotGaslimit := eth.BlockGasLimit(ctx) + s.Require().Equal(tt.wantGasLimit, gotGaslimit) + }) + } +} diff --git a/eth/hdpath.go b/eth/hdpath.go new file mode 100644 index 000000000..c563e6fd0 --- /dev/null +++ b/eth/hdpath.go @@ -0,0 +1,33 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eth + +import ( + ethaccounts "github.com/ethereum/go-ethereum/accounts" +) + +var ( + // Bip44CoinType satisfies EIP84. See https://github.com/ethereum/EIPs/issues/84 for more info. + Bip44CoinType uint32 = 60 + + // BIP44HDPath is the default BIP44 HD path used on Ethereum. + BIP44HDPath = ethaccounts.DefaultBaseDerivationPath.String() +) + +type ( + HDPathIterator func() ethaccounts.DerivationPath +) + +// HDPathIterator receives a base path as a string and a boolean for the desired iterator type and +// returns a function that iterates over the base HD path, returning the string. +func NewHDPathIterator(basePath string, ledgerIter bool) (HDPathIterator, error) { + hdPath, err := ethaccounts.ParseDerivationPath(basePath) + if err != nil { + return nil, err + } + + if ledgerIter { + return ethaccounts.LedgerLiveIterator(hdPath), nil + } + + return ethaccounts.DefaultIterator(hdPath), nil +} diff --git a/eth/indexer.go b/eth/indexer.go new file mode 100644 index 000000000..bf2dda109 --- /dev/null +++ b/eth/indexer.go @@ -0,0 +1,20 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package eth + +import ( + abci "github.com/cometbft/cometbft/abci/types" + tmtypes "github.com/cometbft/cometbft/types" + "github.com/ethereum/go-ethereum/common" +) + +// EVMTxIndexer defines the interface of custom eth tx indexer. +type EVMTxIndexer interface { + // LastIndexedBlock returns -1 if indexer db is empty + LastIndexedBlock() (int64, error) + IndexBlock(*tmtypes.Block, []*abci.ResponseDeliverTx) error + + // GetByTxHash returns nil if tx not found. + GetByTxHash(common.Hash) (*TxResult, error) + // GetByBlockAndIndex returns nil if tx not found. + GetByBlockAndIndex(int64, int32) (*TxResult, error) +} diff --git a/eth/indexer.pb.go b/eth/indexer.pb.go new file mode 100644 index 000000000..5fbf5d2af --- /dev/null +++ b/eth/indexer.pb.go @@ -0,0 +1,488 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: eth/types/v1/indexer.proto + +package eth + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// TxResult is the value stored in eth tx indexer +type TxResult struct { + // height of the blockchain + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + // tx_index is the index of the block transaction. It is not the index of an + // "internal transaction" + TxIndex uint32 `protobuf:"varint,2,opt,name=tx_index,json=txIndex,proto3" json:"tx_index,omitempty"` + // msg_index in a batch transaction + MsgIndex uint32 `protobuf:"varint,3,opt,name=msg_index,json=msgIndex,proto3" json:"msg_index,omitempty"` + // eth_tx_index is the index in the list of valid eth tx in the block. Said + // another way, it is the index of the transaction list returned by + // eth_getBlock API. + EthTxIndex int32 `protobuf:"varint,4,opt,name=eth_tx_index,json=ethTxIndex,proto3" json:"eth_tx_index,omitempty"` + // failed is true if the eth transaction did not succeed + Failed bool `protobuf:"varint,5,opt,name=failed,proto3" json:"failed,omitempty"` + // gas_used by the transaction. If it exceeds the block gas limit, + // it's set to gas limit, which is what's actually deducted by ante handler. + GasUsed uint64 `protobuf:"varint,6,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + // cumulative_gas_used specifies the cumulated amount of gas used for all + // processed messages within the current batch transaction. + CumulativeGasUsed uint64 `protobuf:"varint,7,opt,name=cumulative_gas_used,json=cumulativeGasUsed,proto3" json:"cumulative_gas_used,omitempty"` +} + +func (m *TxResult) Reset() { *m = TxResult{} } +func (m *TxResult) String() string { return proto.CompactTextString(m) } +func (*TxResult) ProtoMessage() {} +func (*TxResult) Descriptor() ([]byte, []int) { + return fileDescriptor_6eb4a496a6639a52, []int{0} +} +func (m *TxResult) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TxResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TxResult.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TxResult) XXX_Merge(src proto.Message) { + xxx_messageInfo_TxResult.Merge(m, src) +} +func (m *TxResult) XXX_Size() int { + return m.Size() +} +func (m *TxResult) XXX_DiscardUnknown() { + xxx_messageInfo_TxResult.DiscardUnknown(m) +} + +var xxx_messageInfo_TxResult proto.InternalMessageInfo + +func init() { + proto.RegisterType((*TxResult)(nil), "eth.types.v1.TxResult") +} + +func init() { proto.RegisterFile("eth/types/v1/indexer.proto", fileDescriptor_6eb4a496a6639a52) } + +var fileDescriptor_6eb4a496a6639a52 = []byte{ + // 303 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x90, 0x31, 0x4e, 0xc3, 0x30, + 0x18, 0x85, 0x63, 0xda, 0xa6, 0xc1, 0x82, 0x81, 0x80, 0xaa, 0x50, 0xa4, 0x10, 0x21, 0x86, 0x4c, + 0xb1, 0x0a, 0x1b, 0x03, 0x03, 0x0c, 0x88, 0x85, 0xc1, 0x2a, 0x0b, 0x4b, 0x94, 0x36, 0x3f, 0xb6, + 0xa5, 0xa6, 0xa9, 0xea, 0xdf, 0x51, 0xb8, 0x01, 0x23, 0x47, 0xe0, 0x38, 0x8c, 0x1d, 0x19, 0x51, + 0x2b, 0xee, 0x81, 0xe2, 0x46, 0x65, 0x7b, 0x4f, 0xdf, 0xf7, 0x6c, 0xcb, 0x74, 0x08, 0x28, 0x19, + 0xbe, 0x2d, 0x40, 0xb3, 0x6a, 0xc4, 0xd4, 0x3c, 0x87, 0x1a, 0x96, 0xc9, 0x62, 0x59, 0x62, 0xe9, + 0x1f, 0x00, 0xca, 0xc4, 0xb2, 0xa4, 0x1a, 0x0d, 0x4f, 0x44, 0x29, 0x4a, 0x0b, 0x58, 0x93, 0xb6, + 0xce, 0xc5, 0x2f, 0xa1, 0xde, 0xb8, 0xe6, 0xa0, 0xcd, 0x0c, 0xfd, 0x01, 0x75, 0x25, 0x28, 0x21, + 0x31, 0x20, 0x11, 0x89, 0x3b, 0xbc, 0x6d, 0xfe, 0x29, 0xf5, 0xb0, 0x4e, 0xed, 0xe1, 0xc1, 0x5e, + 0x44, 0xe2, 0x43, 0xde, 0xc7, 0xfa, 0xb1, 0xa9, 0xfe, 0x19, 0xdd, 0x2f, 0xb4, 0x68, 0x59, 0xc7, + 0x32, 0xaf, 0xd0, 0x62, 0x0b, 0x23, 0xda, 0x3c, 0x21, 0xdd, 0x6d, 0xbb, 0x11, 0x89, 0x7b, 0x9c, + 0x02, 0xca, 0x71, 0x3b, 0x1f, 0x50, 0xf7, 0x35, 0x53, 0x33, 0xc8, 0x83, 0x5e, 0x44, 0x62, 0x8f, + 0xb7, 0xad, 0xb9, 0x51, 0x64, 0x3a, 0x35, 0x1a, 0xf2, 0xc0, 0x8d, 0x48, 0xdc, 0xe5, 0x7d, 0x91, + 0xe9, 0x67, 0x0d, 0xb9, 0x9f, 0xd0, 0xe3, 0xa9, 0x29, 0xcc, 0x2c, 0x43, 0x55, 0x41, 0xba, 0xb3, + 0xfa, 0xd6, 0x3a, 0xfa, 0x47, 0x0f, 0x5b, 0xff, 0xa6, 0xfb, 0xfe, 0x79, 0xee, 0xdc, 0xdd, 0x7e, + 0xad, 0x43, 0xb2, 0x5a, 0x87, 0xe4, 0x67, 0x1d, 0x92, 0x8f, 0x4d, 0xe8, 0xac, 0x36, 0xa1, 0xf3, + 0xbd, 0x09, 0x9d, 0x97, 0x4b, 0xa1, 0x50, 0x9a, 0x49, 0x32, 0x2d, 0x0b, 0xf6, 0xa4, 0x26, 0x6a, + 0x69, 0xee, 0x65, 0xa6, 0xe6, 0x6c, 0x6e, 0x33, 0xab, 0xae, 0x18, 0xa0, 0x9c, 0xb8, 0xf6, 0xbb, + 0xae, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x30, 0x91, 0xa4, 0x70, 0x01, 0x00, 0x00, +} + +func (m *TxResult) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TxResult) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TxResult) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CumulativeGasUsed != 0 { + i = encodeVarintIndexer(dAtA, i, uint64(m.CumulativeGasUsed)) + i-- + dAtA[i] = 0x38 + } + if m.GasUsed != 0 { + i = encodeVarintIndexer(dAtA, i, uint64(m.GasUsed)) + i-- + dAtA[i] = 0x30 + } + if m.Failed { + i-- + if m.Failed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.EthTxIndex != 0 { + i = encodeVarintIndexer(dAtA, i, uint64(m.EthTxIndex)) + i-- + dAtA[i] = 0x20 + } + if m.MsgIndex != 0 { + i = encodeVarintIndexer(dAtA, i, uint64(m.MsgIndex)) + i-- + dAtA[i] = 0x18 + } + if m.TxIndex != 0 { + i = encodeVarintIndexer(dAtA, i, uint64(m.TxIndex)) + i-- + dAtA[i] = 0x10 + } + if m.Height != 0 { + i = encodeVarintIndexer(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintIndexer(dAtA []byte, offset int, v uint64) int { + offset -= sovIndexer(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *TxResult) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovIndexer(uint64(m.Height)) + } + if m.TxIndex != 0 { + n += 1 + sovIndexer(uint64(m.TxIndex)) + } + if m.MsgIndex != 0 { + n += 1 + sovIndexer(uint64(m.MsgIndex)) + } + if m.EthTxIndex != 0 { + n += 1 + sovIndexer(uint64(m.EthTxIndex)) + } + if m.Failed { + n += 2 + } + if m.GasUsed != 0 { + n += 1 + sovIndexer(uint64(m.GasUsed)) + } + if m.CumulativeGasUsed != 0 { + n += 1 + sovIndexer(uint64(m.CumulativeGasUsed)) + } + return n +} + +func sovIndexer(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozIndexer(x uint64) (n int) { + return sovIndexer(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *TxResult) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIndexer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TxResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TxResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIndexer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxIndex", wireType) + } + m.TxIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIndexer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxIndex |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MsgIndex", wireType) + } + m.MsgIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIndexer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MsgIndex |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EthTxIndex", wireType) + } + m.EthTxIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIndexer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EthTxIndex |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Failed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIndexer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Failed = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType) + } + m.GasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIndexer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CumulativeGasUsed", wireType) + } + m.CumulativeGasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIndexer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CumulativeGasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipIndexer(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIndexer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipIndexer(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowIndexer + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowIndexer + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowIndexer + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthIndexer + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupIndexer + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthIndexer + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthIndexer = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowIndexer = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupIndexer = fmt.Errorf("proto: unexpected end of group") +) diff --git a/eth/indexer/evm_tx_indexer.go b/eth/indexer/evm_tx_indexer.go new file mode 100644 index 000000000..0b1ba8966 --- /dev/null +++ b/eth/indexer/evm_tx_indexer.go @@ -0,0 +1,252 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package indexer + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/log" + tmtypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +const ( + KeyPrefixTxHash = 1 + KeyPrefixTxIndex = 2 + + // TxIndexKeyLength is the length of tx-index key + TxIndexKeyLength = 1 + 8 + 8 +) + +var _ eth.EVMTxIndexer = &EVMTxIndexer{} + +// EVMTxIndexer implements a eth tx indexer on a KV db. +type EVMTxIndexer struct { + db dbm.DB + logger log.Logger + clientCtx client.Context +} + +// NewEVMTxIndexer creates the EVMTxIndexer +func NewEVMTxIndexer(db dbm.DB, logger log.Logger, clientCtx client.Context) *EVMTxIndexer { + return &EVMTxIndexer{db, logger, clientCtx} +} + +// IndexBlock index all the eth txs in a block through the following steps: +// - Iterates over all the Txs in Block +// - Parses eth Tx infos from cosmos-sdk events for every TxResult +// - Iterates over all the messages of the Tx +// - Builds and stores indexer.TxResult based on parsed events for every message +func (indexer *EVMTxIndexer) IndexBlock(block *tmtypes.Block, txResults []*abci.ResponseDeliverTx) error { + height := block.Header.Height + + batch := indexer.db.NewBatch() + defer batch.Close() + + // record index of valid eth tx during the iteration + var ethTxIndex int32 + for txIndex, tx := range block.Txs { + result := txResults[txIndex] + isValidEnough, reason := rpc.TxIsValidEnough(result) + if !isValidEnough { + indexer.logger.Debug( + "Skipped indexing of tx", + "reason", reason, + "tm_tx_hash", eth.TmTxHashToString(tx.Hash()), + ) + continue + } + + tx, err := indexer.clientCtx.TxConfig.TxDecoder()(tx) + if err != nil { + indexer.logger.Error("Fail to decode tx", "err", err, "block", height, "txIndex", txIndex) + continue + } + + if !isEthTx(tx) { + continue + } + + txs, err := rpc.ParseTxResult(result, tx) + if err != nil { + indexer.logger.Error("Fail to parse event", "err", err, "block", height, "txIndex", txIndex) + continue + } + + var cumulativeGasUsed uint64 + for msgIndex, msg := range tx.GetMsgs() { + ethMsg := msg.(*evm.MsgEthereumTx) + txHash := common.HexToHash(ethMsg.Hash) + + txResult := eth.TxResult{ + Height: height, + TxIndex: uint32(txIndex), + MsgIndex: uint32(msgIndex), + EthTxIndex: ethTxIndex, + } + if result.Code != abci.CodeTypeOK { + // exceeds block gas limit scenario, set gas used to gas limit because that's what's charged by ante handler. + // some old versions don't emit any events, so workaround here directly. + txResult.GasUsed = ethMsg.GetGas() + txResult.Failed = true + } else { + parsedTx := txs.GetTxByMsgIndex(msgIndex) + if parsedTx == nil { + indexer.logger.Error("msg index not found in events", "msgIndex", msgIndex) + continue + } + if parsedTx.EthTxIndex >= 0 && parsedTx.EthTxIndex != ethTxIndex { + indexer.logger.Error( + "eth tx index don't match", + "expect", ethTxIndex, + "found", parsedTx.EthTxIndex, + "height", height, + ) + } + txResult.GasUsed = parsedTx.GasUsed + txResult.Failed = parsedTx.Failed + } + + cumulativeGasUsed += txResult.GasUsed + txResult.CumulativeGasUsed = cumulativeGasUsed + ethTxIndex++ + + if err := saveTxResult(indexer.clientCtx.Codec, batch, txHash, &txResult); err != nil { + return errorsmod.Wrapf(err, "IndexBlock %d", height) + } + } + } + if err := batch.Write(); err != nil { + return errorsmod.Wrapf(err, "IndexBlock %d, write batch", block.Height) + } + return nil +} + +// LastIndexedBlock returns the latest indexed block number, returns -1 if db is empty +func (indexer *EVMTxIndexer) LastIndexedBlock() (int64, error) { + return LoadLastBlock(indexer.db) +} + +// FirstIndexedBlock returns the first indexed block number, returns -1 if db is empty +func (indexer *EVMTxIndexer) FirstIndexedBlock() (int64, error) { + return LoadFirstBlock(indexer.db) +} + +// GetByTxHash finds eth tx by eth tx hash +func (indexer *EVMTxIndexer) GetByTxHash(hash common.Hash) (*eth.TxResult, error) { + bz, err := indexer.db.Get(TxHashKey(hash)) + if err != nil { + return nil, errorsmod.Wrapf(err, "GetByTxHash %s", hash.Hex()) + } + if len(bz) == 0 { + return nil, fmt.Errorf("tx not found, hash: %s", hash.Hex()) + } + var txKey eth.TxResult + if err := indexer.clientCtx.Codec.Unmarshal(bz, &txKey); err != nil { + return nil, errorsmod.Wrapf(err, "GetByTxHash %s", hash.Hex()) + } + return &txKey, nil +} + +// GetByBlockAndIndex finds eth tx by block number and eth tx index +func (indexer *EVMTxIndexer) GetByBlockAndIndex(blockNumber int64, txIndex int32) (*eth.TxResult, error) { + bz, err := indexer.db.Get(TxIndexKey(blockNumber, txIndex)) + if err != nil { + return nil, errorsmod.Wrapf(err, "GetByBlockAndIndex %d %d", blockNumber, txIndex) + } + if len(bz) == 0 { + return nil, fmt.Errorf("tx not found, block: %d, eth-index: %d", blockNumber, txIndex) + } + return indexer.GetByTxHash(common.BytesToHash(bz)) +} + +// TxHashKey returns the key for db entry: `tx hash -> tx result struct` +func TxHashKey(hash common.Hash) []byte { + return append([]byte{KeyPrefixTxHash}, hash.Bytes()...) +} + +// TxIndexKey returns the key for db entry: `(block number, tx index) -> tx hash` +func TxIndexKey(blockNumber int64, txIndex int32) []byte { + bz1 := sdk.Uint64ToBigEndian(uint64(blockNumber)) + bz2 := sdk.Uint64ToBigEndian(uint64(txIndex)) + return append(append([]byte{KeyPrefixTxIndex}, bz1...), bz2...) +} + +// LoadLastBlock returns the latest indexed block number, returns -1 if db is empty +func LoadLastBlock(db dbm.DB) (int64, error) { + it, err := db.ReverseIterator([]byte{KeyPrefixTxIndex}, []byte{KeyPrefixTxIndex + 1}) + if err != nil { + return 0, errorsmod.Wrap(err, "LoadLastBlock") + } + defer it.Close() + if !it.Valid() { + return -1, nil + } + return parseBlockNumberFromKey(it.Key()) +} + +// LoadFirstBlock loads the first indexed block, returns -1 if db is empty +func LoadFirstBlock(db dbm.DB) (int64, error) { + it, err := db.Iterator([]byte{KeyPrefixTxIndex}, []byte{KeyPrefixTxIndex + 1}) + if err != nil { + return 0, errorsmod.Wrap(err, "LoadFirstBlock") + } + defer it.Close() + if !it.Valid() { + return -1, nil + } + return parseBlockNumberFromKey(it.Key()) +} + +// CloseDBAndExit should be called upon stopping the indexer +func (indexer *EVMTxIndexer) CloseDBAndExit() error { + indexer.logger.Info("Closing EVMTxIndexer DB") + err := indexer.db.Close() + if err != nil { + return errorsmod.Wrap(err, "CloseDBAndExit") + } + return nil +} + +// isEthTx check if the tx is an eth tx +func isEthTx(tx sdk.Tx) bool { + extTx, ok := tx.(authante.HasExtensionOptionsTx) + if !ok { + return false + } + opts := extTx.GetExtensionOptions() + if len(opts) != 1 || opts[0].GetTypeUrl() != "/eth.evm.v1.ExtensionOptionsEthereumTx" { + return false + } + return true +} + +// saveTxResult index the txResult into the kv db batch +func saveTxResult(codec codec.Codec, batch dbm.Batch, txHash common.Hash, txResult *eth.TxResult) error { + bz := codec.MustMarshal(txResult) + if err := batch.Set(TxHashKey(txHash), bz); err != nil { + return errorsmod.Wrap(err, "set tx-hash key") + } + if err := batch.Set(TxIndexKey(txResult.Height, txResult.EthTxIndex), txHash.Bytes()); err != nil { + return errorsmod.Wrap(err, "set tx-index key") + } + return nil +} + +func parseBlockNumberFromKey(key []byte) (int64, error) { + if len(key) != TxIndexKeyLength { + return 0, fmt.Errorf("wrong tx index key length, expect: %d, got: %d", TxIndexKeyLength, len(key)) + } + + return int64(sdk.BigEndianToUint64(key[1:9])), nil +} diff --git a/eth/indexer/evm_tx_indexer_test.go b/eth/indexer/evm_tx_indexer_test.go new file mode 100644 index 000000000..e950e571c --- /dev/null +++ b/eth/indexer/evm_tx_indexer_test.go @@ -0,0 +1,202 @@ +package indexer_test + +import ( + "fmt" + "math/big" + "testing" + + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + tmlog "github.com/cometbft/cometbft/libs/log" + tmtypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" + "github.com/NibiruChain/nibiru/v2/eth/indexer" + "github.com/NibiruChain/nibiru/v2/x/evm" + evmtest "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func TestEVMTxIndexer(t *testing.T) { + priv, err := ethsecp256k1.GenerateKey() + require.NoError(t, err) + from := common.BytesToAddress(priv.PubKey().Address().Bytes()) + signer := evmtest.NewSigner(priv) + ethSigner := gethcore.LatestSignerForChainID(nil) + + to := common.BigToAddress(big.NewInt(1)) + ethTxParams := evm.EvmTxArgs{ + Nonce: 0, + To: &to, + Amount: big.NewInt(1000), + GasLimit: 21000, + } + tx := evm.NewTx(ðTxParams) + tx.From = from.Hex() + require.NoError(t, tx.Sign(ethSigner, signer)) + txHash := tx.AsTransaction().Hash() + + encCfg := app.MakeEncodingConfig() + eth.RegisterInterfaces(encCfg.InterfaceRegistry) + evm.RegisterInterfaces(encCfg.InterfaceRegistry) + clientCtx := client.Context{}. + WithTxConfig(encCfg.TxConfig). + WithCodec(encCfg.Codec) + + // build cosmos-sdk wrapper tx + validEVMTx, err := tx.BuildTx(clientCtx.TxConfig.NewTxBuilder(), eth.EthBaseDenom) + require.NoError(t, err) + validEVMTxBz, err := clientCtx.TxConfig.TxEncoder()(validEVMTx) + require.NoError(t, err) + + // build an invalid wrapper tx + builder := clientCtx.TxConfig.NewTxBuilder() + require.NoError(t, builder.SetMsgs(tx)) + invalidTx := builder.GetTx() + invalidTxBz, err := clientCtx.TxConfig.TxEncoder()(invalidTx) + require.NoError(t, err) + + testCases := []struct { + name string + block *tmtypes.Block + blockResult []*abci.ResponseDeliverTx + expSuccess bool + }{ + { + "happy, only pending_ethereum_tx presents", + &tmtypes.Block{ + Header: tmtypes.Header{Height: 1}, + Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}, + }, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + { + Type: evm.PendingEthereumTxEvent, + Attributes: []abci.EventAttribute{ + {Key: evm.PendingEthereumTxEventAttrEthHash, Value: txHash.Hex()}, + {Key: evm.PendingEthereumTxEventAttrIndex, Value: "0"}, + }, + }, + }, + }, + }, + true, + }, + { + "happy: code 0, pending_ethereum_tx and typed EventEthereumTx present", + &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}}, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + { + Type: evm.PendingEthereumTxEvent, + Attributes: []abci.EventAttribute{ + {Key: evm.PendingEthereumTxEventAttrEthHash, Value: txHash.Hex()}, + {Key: evm.PendingEthereumTxEventAttrIndex, Value: "0"}, + }, + }, + { + Type: evm.TypeUrlEventEthereumTx, + Attributes: []abci.EventAttribute{ + {Key: "amount", Value: `"1000"`}, + {Key: "gas_used", Value: `"21000"`}, + {Key: "index", Value: `"0"`}, + {Key: "hash", Value: `"14A84ED06282645EFBF080E0B7ED80D8D8D6A36337668A12B5F229F81CDD3F57"`}, + {Key: "eth_hash", Value: fmt.Sprintf(`"%s"`, txHash.Hex())}, + }, + }, + }, + }, + }, + true, + }, + { + "happy: code 11, exceed block gas limit", + &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}}, + []*abci.ResponseDeliverTx{ + { + Code: 11, + Log: "out of gas in location: block gas meter; gasWanted: 21000", + Events: []abci.Event{}, + }, + }, + true, + }, + { + "sad: failed eth tx", + &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}}, + []*abci.ResponseDeliverTx{ + { + Code: 15, + Log: "nonce mismatch", + Events: []abci.Event{}, + }, + }, + false, + }, + { + "sad: invalid events", + &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{validEVMTxBz}}}, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{}, + }, + }, + false, + }, + { + "sad: not eth tx", + &tmtypes.Block{Header: tmtypes.Header{Height: 1}, Data: tmtypes.Data{Txs: []tmtypes.Tx{invalidTxBz}}}, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{}, + }, + }, + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + db := dbm.NewMemDB() + idxer := indexer.NewEVMTxIndexer(db, tmlog.NewNopLogger(), clientCtx) + + err = idxer.IndexBlock(tc.block, tc.blockResult) + require.NoError(t, err) + if !tc.expSuccess { + first, err := idxer.FirstIndexedBlock() + require.NoError(t, err) + require.Equal(t, int64(-1), first) + + last, err := idxer.LastIndexedBlock() + require.NoError(t, err) + require.Equal(t, int64(-1), last) + } else { + first, err := idxer.FirstIndexedBlock() + require.NoError(t, err) + require.Equal(t, tc.block.Header.Height, first) + + last, err := idxer.LastIndexedBlock() + require.NoError(t, err) + require.Equal(t, tc.block.Header.Height, last) + + res1, err := idxer.GetByTxHash(txHash) + require.NoError(t, err) + require.NotNil(t, res1) + res2, err := idxer.GetByBlockAndIndex(1, 0) + require.NoError(t, err) + require.Equal(t, res1, res2) + } + }) + } +} diff --git a/eth/rpc/addrlocker.go b/eth/rpc/addrlocker.go new file mode 100644 index 000000000..3722a447e --- /dev/null +++ b/eth/rpc/addrlocker.go @@ -0,0 +1,48 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpc + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +// AddrLocker is a mutex (mutual exclusion lock) structure used to avoid querying +// outdated account data. It prevents data races by allowing only one goroutine +// to access critical sections at a time. +type AddrLocker struct { + // mu protects access to the locks map + mu sync.Mutex + locks map[common.Address]*sync.Mutex +} + +// lock returns the mutex lock of the given Ethereum address. If no mutex exists +// for the address, it creates a new one. This function ensures that each address +// has exactly one mutex associated with it, and it is thread-safe. +// +// The returned mutex is not locked; callers are responsible for locking and +// unlocking it as necessary. +func (l *AddrLocker) lock(address common.Address) *sync.Mutex { + l.mu.Lock() + defer l.mu.Unlock() + if l.locks == nil { + l.locks = make(map[common.Address]*sync.Mutex) + } + if _, ok := l.locks[address]; !ok { + l.locks[address] = new(sync.Mutex) + } + return l.locks[address] +} + +// LockAddr acquires the mutex for a specific address, blocking if it is already +// held by another goroutine. The mutex lock prevents an identical nonce from +// being read again during the time that the first transaction is being signed. +func (l *AddrLocker) LockAddr(address common.Address) { + l.lock(address).Lock() +} + +// UnlockAddr unlocks the mutex for a specific address, allowing other goroutines +// to acquire it. +func (l *AddrLocker) UnlockAddr(address common.Address) { + l.lock(address).Unlock() +} diff --git a/eth/rpc/addrlocker_test.go b/eth/rpc/addrlocker_test.go new file mode 100644 index 000000000..97b9bea94 --- /dev/null +++ b/eth/rpc/addrlocker_test.go @@ -0,0 +1,126 @@ +package rpc_test + +import ( + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" + + rpc "github.com/NibiruChain/nibiru/v2/eth/rpc" +) + +type SuiteAddrLocker struct { + suite.Suite +} + +func TestSuiteAddrLocker(t *testing.T) { + suite.Run(t, new(SuiteAddrLocker)) +} + +// TestLockAddr: This test checks that the lock mechanism prevents multiple +// goroutines from entering critical sections of code simultaneously for the same +// address. +func (s *SuiteAddrLocker) TestLockAddr() { + // Setup: Lock the address + locker := &rpc.AddrLocker{} + addr := common.HexToAddress("0x123") + locker.LockAddr(addr) + + // Concurrent Lock Attempt: Attempt to lock again in a separate goroutine. If + // the initial lock is effective, this attempt should block and not complete + // immediately. + done := make(chan bool) + go func() { + locker.LockAddr(addr) // This should block if the first lock is effective + done <- true + }() + + // Assertion: A select statement is used to check if the channel receives a + // value, which would indicate the lock did not block as expected. + select { + case <-done: + s.Fail("LockAddr did not block the second call as expected") + default: + // expected behavior, continue test + } + + // Cleanup: Unlock and allow the goroutine to proceed + locker.UnlockAddr(addr) + <-done // Ensure goroutine completes +} + +func (s *SuiteAddrLocker) TestUnlockAddr() { + // Setup: Lock the address + locker := &rpc.AddrLocker{} + addr := common.HexToAddress("0x123") + locker.LockAddr(addr) + + locker.UnlockAddr(addr) + + // Try re-locking to test if unlock was successful + locked := make(chan bool) + go func() { + locker.LockAddr(addr) // This should not block if unlock worked + locked <- true + locker.UnlockAddr(addr) + }() + + select { + case <-locked: + // expected behavior, continue + case <-time.After(time.Second): + s.Fail("UnlockAddr did not effectively unlock the mutex") + } +} + +func (s *SuiteAddrLocker) TestMultipleAddresses() { + locker := &rpc.AddrLocker{} + addr1 := common.HexToAddress("0x123") + addr2 := common.HexToAddress("0x456") + + locker.LockAddr(addr1) + locked := make(chan bool) + + go func() { + locker.LockAddr(addr2) // This should not block if locks are address-specific + locked <- true + locker.UnlockAddr(addr2) + }() + + select { + case <-locked: + // expected behavior, continue + case <-time.After(time.Second): + s.Fail("Locks are not address-specific as expected") + } + + locker.UnlockAddr(addr1) +} + +// TestConcurrentAccess: Tests the system's behavior under high concurrency, +// specifically ensuring that the lock can handle multiple locking and unlocking +// operations on the same address without leading to race conditions or +// deadlocks. +func (s *SuiteAddrLocker) TestConcurrentAccess() { + locker := &rpc.AddrLocker{} + addr := common.HexToAddress("0x789") + var wg sync.WaitGroup + + // Spawn 100 goroutines, each locking and unlocking the same address. + // Each routine will hod the lock briefly to simulate work done during the + // lock (like an Ethereum query). + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + locker.LockAddr(addr) + time.Sleep(time.Millisecond * 5) // Simulate work + locker.UnlockAddr(addr) + wg.Done() + }() + } + + // Cleanup: Wait for all goroutines to complete + wg.Wait() +} diff --git a/eth/rpc/backend/account_info.go b/eth/rpc/backend/account_info.go new file mode 100644 index 000000000..a0c0caceb --- /dev/null +++ b/eth/rpc/backend/account_info.go @@ -0,0 +1,231 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "fmt" + "math" + "math/big" + + errorsmod "cosmossdk.io/errors" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// GetCode returns the contract code at the given address and block number. +func (b *Backend) GetCode( + address gethcommon.Address, blockNrOrHash rpc.BlockNumberOrHash, +) (hexutil.Bytes, error) { + blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash) + if err != nil { + return nil, err + } + + req := &evm.QueryCodeRequest{ + Address: address.String(), + } + + res, err := b.queryClient.Code(rpc.NewContextWithHeight(blockNum.Int64()), req) + if err != nil { + return nil, err + } + + return res.Code, nil +} + +// GetProof returns an account object with proof and any storage proofs +func (b *Backend) GetProof( + address gethcommon.Address, + storageKeys []string, + blockNrOrHash rpc.BlockNumberOrHash, +) (*rpc.AccountResult, error) { + blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash) + if err != nil { + return nil, err + } + + height := blockNum.Int64() + + _, err = b.TendermintBlockByNumber(blockNum) + if err != nil { + // the error message imitates geth behavior + return nil, errors.New("header not found") + } + ctx := rpc.NewContextWithHeight(height) + + // if the height is equal to zero, meaning the query condition of the block + // is either "pending" or "latest" + if height == 0 { + bn, err := b.BlockNumber() + if err != nil { + return nil, err + } + + if bn > math.MaxInt64 { + return nil, fmt.Errorf("not able to query block number greater than MaxInt64") + } + + height = int64(bn) //#nosec G701 -- checked for int overflow already + } + + clientCtx := b.clientCtx.WithHeight(height) + + // query storage proofs + storageProofs := make([]rpc.StorageResult, len(storageKeys)) + + for i, key := range storageKeys { + hexKey := gethcommon.HexToHash(key) + valueBz, proof, err := b.queryClient.GetProof( + clientCtx, + evm.StoreKey, + evm.StateKey(address, hexKey.Bytes()), + ) + if err != nil { + return nil, err + } + + storageProofs[i] = rpc.StorageResult{ + Key: key, + Value: (*hexutil.Big)(new(big.Int).SetBytes(valueBz)), + Proof: GetHexProofs(proof), + } + } + + // query EVM account + req := &evm.QueryEthAccountRequest{ + Address: address.String(), + } + + res, err := b.queryClient.EthAccount(ctx, req) + if err != nil { + return nil, err + } + + // query account proofs + accountKey := authtypes.AddressStoreKey(address.Bytes()) + _, proof, err := b.queryClient.GetProof(clientCtx, authtypes.StoreKey, accountKey) + if err != nil { + return nil, err + } + + balance, ok := sdkmath.NewIntFromString(res.BalanceWei) + if !ok { + return nil, errors.New("invalid balance") + } + + return &rpc.AccountResult{ + Address: address, + AccountProof: GetHexProofs(proof), + Balance: (*hexutil.Big)(balance.BigInt()), + CodeHash: gethcommon.HexToHash(res.CodeHash), + Nonce: hexutil.Uint64(res.Nonce), + // NOTE: The StorageHash is blank. Consider whether this is useful in the + // future. Currently, all storage is handles by persistent and transient + // `sdk.KVStore` objects. + StorageHash: gethcommon.Hash{}, + StorageProof: storageProofs, + }, nil +} + +// GetStorageAt returns the contract storage at the given address, block number, and key. +func (b *Backend) GetStorageAt(address gethcommon.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { + blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash) + if err != nil { + return nil, err + } + + req := &evm.QueryStorageRequest{ + Address: address.String(), + Key: key, + } + + res, err := b.queryClient.Storage(rpc.NewContextWithHeight(blockNum.Int64()), req) + if err != nil { + return nil, err + } + + value := gethcommon.HexToHash(res.Value) + return value.Bytes(), nil +} + +// GetBalance returns the provided account's balance up to the provided block number. +func (b *Backend) GetBalance( + address gethcommon.Address, + blockNrOrHash rpc.BlockNumberOrHash, +) (*hexutil.Big, error) { + blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash) + if err != nil { + return nil, err + } + + req := &evm.QueryBalanceRequest{ + Address: address.String(), + } + + _, err = b.TendermintBlockByNumber(blockNum) + if err != nil { + return nil, err + } + + res, err := b.queryClient.Balance(rpc.NewContextWithHeight(blockNum.Int64()), req) + if err != nil { + return nil, err + } + + val, ok := sdkmath.NewIntFromString(res.BalanceWei) + if !ok { + return nil, fmt.Errorf("invalid balance: %s", res.BalanceWei) + } + + // balance can only be negative in case of pruned node + if val.IsNegative() { + return nil, errors.New("couldn't fetch balance. Node state is pruned") + } + + return (*hexutil.Big)(val.BigInt()), nil +} + +// GetTransactionCount returns the number of transactions at the given address up to the given block number. +func (b *Backend) GetTransactionCount(address gethcommon.Address, blockNum rpc.BlockNumber) (*hexutil.Uint64, error) { + n := hexutil.Uint64(0) + bn, err := b.BlockNumber() + if err != nil { + return &n, err + } + height := blockNum.Int64() + + currentHeight := int64(bn) //#nosec G701 -- checked for int overflow already + if height > currentHeight { + return &n, errorsmod.Wrapf( + sdkerrors.ErrInvalidHeight, + "cannot query with height in the future (current: %d, queried: %d); please provide a valid height", + currentHeight, height, + ) + } + // Get nonce (sequence) from account + from := sdk.AccAddress(address.Bytes()) + accRet := b.clientCtx.AccountRetriever + + err = accRet.EnsureExists(b.clientCtx, from) + if err != nil { + // account doesn't exist yet, return 0 + return &n, nil + } + + includePending := blockNum == rpc.EthPendingBlockNumber + nonce, err := b.getAccountNonce(address, includePending, blockNum.Int64(), b.logger) + if err != nil { + return nil, err + } + + n = hexutil.Uint64(nonce) + return &n, nil +} diff --git a/eth/rpc/backend/account_info_test.go b/eth/rpc/backend/account_info_test.go new file mode 100644 index 000000000..66491320a --- /dev/null +++ b/eth/rpc/backend/account_info_test.go @@ -0,0 +1,202 @@ +package backend_test + +import ( + "math/big" + + gethcommon "github.com/ethereum/go-ethereum/common" + "golang.org/x/crypto/sha3" + + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + + rpc "github.com/NibiruChain/nibiru/v2/eth/rpc" +) + +func (s *BackendSuite) TestGetCode() { + testCases := []struct { + name string + contractAddr gethcommon.Address + blockNumber rpc.BlockNumber + codeFound bool + }{ + { + name: "happy: valid contract address", + contractAddr: testContractAddress, + blockNumber: deployContractBlockNumber, + codeFound: true, + }, + { + name: "sad: not a contract address", + contractAddr: s.fundedAccEthAddr, + blockNumber: deployContractBlockNumber, + codeFound: false, + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + code, err := s.backend.GetCode( + tc.contractAddr, + rpc.BlockNumberOrHash{ + BlockNumber: &tc.blockNumber, + }, + ) + if !tc.codeFound { + s.Require().Nil(code) + return + } + s.Require().NoError(err) + s.Require().NotNil(code) + }) + } +} + +func (s *BackendSuite) TestGetProof() { + testCases := []struct { + name string + contractAddr gethcommon.Address + blockNumber rpc.BlockNumber + address gethcommon.Address + slot uint64 + wantValue string + }{ + { + name: "happy: balance of the contract deployer", + contractAddr: testContractAddress, + address: s.fundedAccEthAddr, + blockNumber: deployContractBlockNumber, + slot: 0, // _balances is the first slot in ERC20 + wantValue: "0xd3c21bcecceda1000000", // = 1000000 * (10**18), initial supply + }, + { + name: "sad: address which is not in contract storage", + contractAddr: s.fundedAccEthAddr, + address: recipient, + blockNumber: deployContractBlockNumber, + slot: 0, + wantValue: "0x0", + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + proof, err := s.backend.GetProof( + tc.contractAddr, + []string{generateStorageKey(tc.address, tc.slot)}, + rpc.BlockNumberOrHash{ + BlockNumber: &tc.blockNumber, + }, + ) + s.Require().NoError(err) + s.Require().NotNil(proof) + s.Require().Equal(tc.wantValue, proof.StorageProof[0].Value.String()) + }) + } +} + +func (s *BackendSuite) TestGetStorageAt() { + testCases := []struct { + name string + contractAddr gethcommon.Address + blockNumber rpc.BlockNumber + address gethcommon.Address + slot uint64 + wantValue string + }{ + { + name: "happy: balance of the contract deployer", + contractAddr: testContractAddress, + address: s.fundedAccEthAddr, + blockNumber: deployContractBlockNumber, + // _balances is the first slot in ERC20 + slot: 0, + // = 1000000 * (10**18), initial supply + wantValue: "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000", + }, + { + name: "sad: address which is not in contract storage", + contractAddr: s.fundedAccEthAddr, + address: recipient, + blockNumber: deployContractBlockNumber, + slot: 0, + wantValue: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + value, err := s.backend.GetStorageAt( + tc.contractAddr, + generateStorageKey(tc.address, tc.slot), + rpc.BlockNumberOrHash{ + BlockNumber: &tc.blockNumber, + }, + ) + s.Require().NoError(err) + s.Require().NotNil(value) + s.Require().Equal(tc.wantValue, value.String()) + }) + } +} + +func (s *BackendSuite) TestGetBalance() { + testCases := []struct { + name string + blockNumber rpc.BlockNumber + address gethcommon.Address + wantPositiveBalance bool + }{ + { + name: "happy: funded account balance", + address: s.fundedAccEthAddr, + blockNumber: transferTxBlockNumber, + wantPositiveBalance: true, + }, + { + name: "happy: recipient balance at block 1", + address: recipient, + blockNumber: rpc.NewBlockNumber(big.NewInt(1)), + wantPositiveBalance: false, + }, + { + name: "happy: recipient balance after transfer", + address: recipient, + blockNumber: transferTxBlockNumber, + wantPositiveBalance: true, + }, + { + name: "sad: not existing account", + address: evmtest.NewEthPrivAcc().EthAddr, + blockNumber: transferTxBlockNumber, + wantPositiveBalance: false, + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + balance, err := s.backend.GetBalance( + tc.address, + rpc.BlockNumberOrHash{ + BlockNumber: &tc.blockNumber, + }, + ) + s.Require().NoError(err) + s.Require().NotNil(balance) + if tc.wantPositiveBalance { + s.Require().Greater(balance.ToInt().Int64(), int64(0)) + } else { + s.Require().Equal(balance.ToInt().Int64(), int64(0)) + } + }) + } +} + +// generateStorageKey produces the storage key from address and slot (order of the variable in solidity declaration) +func generateStorageKey(key gethcommon.Address, slot uint64) string { + // Prepare the key and slot as 32-byte values + keyBytes := gethcommon.LeftPadBytes(key.Bytes(), 32) + slotBytes := gethcommon.LeftPadBytes(new(big.Int).SetUint64(slot).Bytes(), 32) + + // Concatenate key and slot + data := append(keyBytes, slotBytes...) + + // Compute the data hash using Keccak256 + hash := sha3.NewLegacyKeccak256() + hash.Write(data) + return gethcommon.BytesToHash(hash.Sum(nil)).Hex() +} diff --git a/eth/rpc/backend/backend.go b/eth/rpc/backend/backend.go new file mode 100644 index 000000000..ecbe94f5f --- /dev/null +++ b/eth/rpc/backend/backend.go @@ -0,0 +1,69 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "context" + "math/big" + + "github.com/cometbft/cometbft/libs/log" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" + + "github.com/NibiruChain/nibiru/v2/app/server/config" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc" +) + +// Backend implements implements the functionality shared within ethereum namespaces +// as defined by [EIP-1474]. +// +// [EIP-1474]: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1474.md +type Backend struct { + ctx context.Context + clientCtx client.Context + queryClient *rpc.QueryClient // gRPC query client + logger log.Logger + chainID *big.Int + cfg config.Config + allowUnprotectedTxs bool + evmTxIndexer eth.EVMTxIndexer +} + +// NewBackend creates a new Backend instance for cosmos and ethereum namespaces +func NewBackend( + ctx *server.Context, + logger log.Logger, + clientCtx client.Context, + allowUnprotectedTxs bool, + evmTxIndexer eth.EVMTxIndexer, +) *Backend { + chainID := eth.ParseEthChainID(clientCtx.ChainID) + appConf, err := config.GetConfig(ctx.Viper) + if err != nil { + panic(err) + } + + return &Backend{ + ctx: context.Background(), + clientCtx: clientCtx, + queryClient: rpc.NewQueryClient(clientCtx), + logger: logger.With("module", "backend"), + chainID: chainID, + cfg: appConf, + allowUnprotectedTxs: allowUnprotectedTxs, + evmTxIndexer: evmTxIndexer, + } +} + +// CosmosBackend: Currently unused. Backend functionality for the shared +// "cosmos" RPC namespace. Implements [BackendI] in combination with [Backend]. +// TODO: feat(eth): Implement the cosmos JSON-RPC defined by [Wallet Connect V2]. +// [Reference ticket]. +// +// [Wallet Connect V2]: https://docs.walletconnect.com/2.0/json-rpc/cosmos. +// [Reference ticket]: https://github.com/NibiruChain/nibiru/issues/2077 +type CosmosBackend interface { + // for Wallet Connect V2: GetAccounts() + // for Wallet Connect V2: SignDirect() + // for Wallet Connect V2: SignAmino() +} diff --git a/eth/rpc/backend/backend_suite_test.go b/eth/rpc/backend/backend_suite_test.go new file mode 100644 index 000000000..dec06920f --- /dev/null +++ b/eth/rpc/backend/backend_suite_test.go @@ -0,0 +1,278 @@ +package backend_test + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "sync" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" +) + +// testMutex is used to synchronize the tests which are broadcasting transactions concurrently +var testMutex sync.Mutex + +var ( + recipient = evmtest.NewEthPrivAcc().EthAddr + amountToSend = evm.NativeToWei(big.NewInt(1)) +) + +var ( + transferTxBlockNumber rpc.BlockNumber + transferTxBlockHash gethcommon.Hash + transferTxHash gethcommon.Hash +) + +var ( + testContractAddress gethcommon.Address + deployContractBlockNumber rpc.BlockNumber +) + +type BackendSuite struct { + suite.Suite + cfg testnetwork.Config + network *testnetwork.Network + node *testnetwork.Validator + fundedAccPrivateKey *ecdsa.PrivateKey + fundedAccEthAddr gethcommon.Address + fundedAccNibiAddr sdk.AccAddress + backend *backend.Backend + ethChainID *big.Int +} + +func TestBackendSuite(t *testing.T) { + testutil.RetrySuiteRunIfDbClosed(t, func() { + suite.Run(t, new(BackendSuite)) + }, 2) +} + +func (s *BackendSuite) SetupSuite() { + testapp.EnsureNibiruPrefix() + + genState := genesis.NewTestGenesisState(app.MakeEncodingConfig()) + homeDir := s.T().TempDir() + s.cfg = testnetwork.BuildNetworkConfig(genState) + network, err := testnetwork.New(s.T(), homeDir, s.cfg) + s.Require().NoError(err) + s.network = network + s.node = network.Validators[0] + s.ethChainID = appconst.GetEthChainID(s.node.ClientCtx.ChainID) + s.backend = s.node.EthRpcBackend + + testAccPrivateKey, _ := crypto.GenerateKey() + s.fundedAccPrivateKey = testAccPrivateKey + s.fundedAccEthAddr = crypto.PubkeyToAddress(testAccPrivateKey.PublicKey) + s.fundedAccNibiAddr = eth.EthAddrToNibiruAddr(s.fundedAccEthAddr) + + funds := sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 100_000_000)) + + txResp, err := testnetwork.FillWalletFromValidator( + s.fundedAccNibiAddr, funds, s.node, eth.EthBaseDenom, + ) + s.Require().NoError(err) + s.Require().NotNil(txResp.TxHash) + s.NoError(s.network.WaitForNextBlock()) + + // Send Transfer TX and use the results in the tests + s.Require().NoError(err) + transferTxHash = s.SendNibiViaEthTransfer(recipient, amountToSend, true /*waitForNextBlock*/) + blockNumber, blockHash, _ := WaitForReceipt(s, transferTxHash) + s.Require().NotNil(blockNumber) + s.Require().NotNil(blockHash) + transferTxBlockNumber = rpc.NewBlockNumber(blockNumber) + transferTxBlockHash = *blockHash + + // Deploy test erc20 contract + deployContractTxHash, contractAddress := s.DeployTestContract(true) + testContractAddress = contractAddress + blockNumber, blockHash, _ = WaitForReceipt(s, deployContractTxHash) + s.Require().NotNil(blockNumber) + s.Require().NotNil(blockHash) + deployContractBlockNumber = rpc.NewBlockNumber(blockNumber) +} + +// SendNibiViaEthTransfer sends nibi using the eth rpc backend +func (s *BackendSuite) SendNibiViaEthTransfer( + to gethcommon.Address, + amount *big.Int, + waitForNextBlock bool, +) gethcommon.Hash { + nonce := s.getCurrentNonce(s.fundedAccEthAddr) + return SendTransaction( + s, + &gethcore.LegacyTx{ + To: &to, + Nonce: uint64(nonce), + Value: amount, + Gas: params.TxGas, + GasPrice: big.NewInt(1), + }, + waitForNextBlock, + ) +} + +// DeployTestContract deploys test erc20 contract +func (s *BackendSuite) DeployTestContract(waitForNextBlock bool) (gethcommon.Hash, gethcommon.Address) { + packedArgs, err := embeds.SmartContract_TestERC20.ABI.Pack("") + s.Require().NoError(err) + bytecodeForCall := append(embeds.SmartContract_TestERC20.Bytecode, packedArgs...) + nonce := s.getCurrentNonce(s.fundedAccEthAddr) + s.Require().NoError(err) + + txHash := SendTransaction( + s, + &gethcore.LegacyTx{ + Nonce: uint64(nonce), + Data: bytecodeForCall, + Gas: 1_500_000, + GasPrice: big.NewInt(1), + }, + waitForNextBlock, + ) + contractAddr := crypto.CreateAddress(s.fundedAccEthAddr, nonce) + return txHash, contractAddr +} + +// SendTransaction signs and sends raw ethereum transaction +func SendTransaction(s *BackendSuite, tx *gethcore.LegacyTx, waitForNextBlock bool) gethcommon.Hash { + signer := gethcore.LatestSignerForChainID(s.ethChainID) + signedTx, err := gethcore.SignNewTx(s.fundedAccPrivateKey, signer, tx) + s.Require().NoError(err) + txBz, err := signedTx.MarshalBinary() + s.Require().NoError(err) + txHash, err := s.backend.SendRawTransaction(txBz) + s.Require().NoError(err) + if waitForNextBlock { + s.Require().NoError(s.network.WaitForNextBlock()) + } + return txHash +} + +// WaitForReceipt waits for a transaction to be included in a block, returns block number, block hash and receipt +func WaitForReceipt(s *BackendSuite, txHash gethcommon.Hash) (*big.Int, *gethcommon.Hash, *backend.TransactionReceipt) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + for { + receipt, err := s.backend.GetTransactionReceipt(txHash) + if err != nil { + return nil, nil, nil + } + if receipt != nil { + return receipt.BlockNumber, &receipt.BlockHash, receipt + } + select { + case <-ctx.Done(): + fmt.Println("Timeout reached, transaction not included in a block yet.") + return nil, nil, nil + default: + time.Sleep(1 * time.Second) + } + } +} + +// getUnibiBalance returns the balance of an address in unibi +func (s *BackendSuite) getUnibiBalance(address gethcommon.Address) *big.Int { + latestBlock := rpc.EthLatestBlockNumber + latestBlockOrHash := rpc.BlockNumberOrHash{BlockNumber: &latestBlock} + balance, err := s.backend.GetBalance(address, latestBlockOrHash) + s.Require().NoError(err) + return evm.WeiToNative(balance.ToInt()) +} + +// getCurrentNonce returns the current nonce of the funded account +func (s *BackendSuite) getCurrentNonce(address gethcommon.Address) uint64 { + nonce, err := s.backend.GetTransactionCount(address, rpc.EthPendingBlockNumber) + s.Require().NoError(err) + + return uint64(*nonce) +} + +// broadcastSDKTx broadcasts the given SDK transaction and returns the response +func (s *BackendSuite) broadcastSDKTx(sdkTx sdk.Tx) *sdk.TxResponse { + txBytes, err := s.backend.ClientCtx().TxConfig.TxEncoder()(sdkTx) + s.Require().NoError(err) + + syncCtx := s.backend.ClientCtx().WithBroadcastMode(flags.BroadcastSync) + rsp, err := syncCtx.BroadcastTx(txBytes) + s.Require().NoError(err) + return rsp +} + +// buildContractCreationTx builds a contract creation transaction +func (s *BackendSuite) buildContractCreationTx(nonce uint64, gasLimit uint64) gethcore.Transaction { + packedArgs, err := embeds.SmartContract_TestERC20.ABI.Pack("") + s.Require().NoError(err) + bytecodeForCall := append(embeds.SmartContract_TestERC20.Bytecode, packedArgs...) + + creationTx := &gethcore.LegacyTx{ + Nonce: nonce, + Data: bytecodeForCall, + Gas: gasLimit, + GasPrice: big.NewInt(1), + } + + signer := gethcore.LatestSignerForChainID(s.ethChainID) + signedTx, err := gethcore.SignNewTx(s.fundedAccPrivateKey, signer, creationTx) + s.Require().NoError(err) + + return *signedTx +} + +// buildContractCallTx builds a contract call transaction +func (s *BackendSuite) buildContractCallTx( + contractAddr gethcommon.Address, + nonce uint64, + gasLimit uint64, +) gethcore.Transaction { + // recipient := crypto.CreateAddress(s.fundedAccEthAddr, 29381) + transferAmount := big.NewInt(100) + + packedArgs, err := embeds.SmartContract_TestERC20.ABI.Pack( + "transfer", + recipient, + transferAmount, + ) + s.Require().NoError(err) + + transferTx := &gethcore.LegacyTx{ + Nonce: nonce, + Data: packedArgs, + Gas: gasLimit, + GasPrice: big.NewInt(1), + To: &contractAddr, + } + + signer := gethcore.LatestSignerForChainID(s.ethChainID) + signedTx, err := gethcore.SignNewTx(s.fundedAccPrivateKey, signer, transferTx) + s.Require().NoError(err) + + return *signedTx +} diff --git a/eth/rpc/backend/blocks.go b/eth/rpc/backend/blocks.go new file mode 100644 index 000000000..7bc5f4b97 --- /dev/null +++ b/eth/rpc/backend/blocks.go @@ -0,0 +1,491 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "fmt" + "math" + "math/big" + "strconv" + + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/gogoproto/proto" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/trie" + "github.com/pkg/errors" + "github.com/status-im/keycard-go/hexutils" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// BlockNumber returns the current block number in abci app state. Because abci +// app state could lag behind from tendermint latest block, it's more stable for +// the client to use the latest block number in abci app state than tendermint rpc. +func (b *Backend) BlockNumber() (hexutil.Uint64, error) { + // do any grpc query, ignore the response and use the returned block height + var header metadata.MD + _, err := b.queryClient.Params(b.ctx, &evm.QueryParamsRequest{}, grpc.Header(&header)) + if err != nil { + return hexutil.Uint64(0), err + } + + blockHeightHeader := header.Get(grpctypes.GRPCBlockHeightHeader) + if headerLen := len(blockHeightHeader); headerLen != 1 { + return 0, fmt.Errorf("unexpected '%s' gRPC header length; got %d, expected: %d", grpctypes.GRPCBlockHeightHeader, headerLen, 1) + } + + height, err := strconv.ParseUint(blockHeightHeader[0], 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse block height: %w", err) + } + + if height > math.MaxInt64 { + return 0, fmt.Errorf("block height %d is greater than max uint64", height) + } + + return hexutil.Uint64(height), nil +} + +// GetBlockByNumber returns the JSON-RPC compatible Ethereum block identified by +// block number. Depending on fullTx it either returns the full transaction +// objects or if false only the hashes of the transactions. +func (b *Backend) GetBlockByNumber(blockNum rpc.BlockNumber, fullTx bool) (map[string]any, error) { + resBlock, err := b.TendermintBlockByNumber(blockNum) + if err != nil { + return nil, nil + } + + // return if requested block height is greater than the current one + if resBlock == nil || resBlock.Block == nil { + return nil, nil + } + + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + b.logger.Debug("failed to fetch block result from Tendermint", "height", blockNum, "error", err.Error()) + return nil, nil + } + + res, err := b.RPCBlockFromTendermintBlock(resBlock, blockRes, fullTx) + if err != nil { + b.logger.Debug("GetEthBlockFromTendermint failed", "height", blockNum, "error", err.Error()) + return nil, err + } + + return res, nil +} + +// GetBlockByHash returns the JSON-RPC compatible Ethereum block identified by +// hash. +func (b *Backend) GetBlockByHash(hash gethcommon.Hash, fullTx bool) (map[string]any, error) { + resBlock, err := b.TendermintBlockByHash(hash) + if err != nil { + return nil, err + } + + if resBlock == nil { + // block not found + return nil, nil + } + + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + b.logger.Debug("failed to fetch block result from Tendermint", "block-hash", hash.String(), "error", err.Error()) + return nil, nil + } + + res, err := b.RPCBlockFromTendermintBlock(resBlock, blockRes, fullTx) + if err != nil { + b.logger.Debug("GetEthBlockFromTendermint failed", "hash", hash, "error", err.Error()) + return nil, err + } + + return res, nil +} + +// GetBlockTransactionCountByHash returns the number of Ethereum transactions in +// the block identified by hash. +func (b *Backend) GetBlockTransactionCountByHash(hash gethcommon.Hash) *hexutil.Uint { + sc, ok := b.clientCtx.Client.(tmrpcclient.SignClient) + if !ok { + b.logger.Error("invalid rpc client") + } + + block, err := sc.BlockByHash(b.ctx, hash.Bytes()) + if err != nil { + b.logger.Debug("block not found", "hash", hash.Hex(), "error", err.Error()) + return nil + } + + if block.Block == nil { + b.logger.Debug("block not found", "hash", hash.Hex()) + return nil + } + + return b.GetBlockTransactionCount(block) +} + +// GetBlockTransactionCountByNumber returns the number of Ethereum transactions +// in the block identified by number. +func (b *Backend) GetBlockTransactionCountByNumber(blockNum rpc.BlockNumber) *hexutil.Uint { + block, err := b.TendermintBlockByNumber(blockNum) + if err != nil { + b.logger.Debug("block not found", "height", blockNum.Int64(), "error", err.Error()) + return nil + } + + if block.Block == nil { + b.logger.Debug("block not found", "height", blockNum.Int64()) + return nil + } + + return b.GetBlockTransactionCount(block) +} + +// GetBlockTransactionCount returns the number of Ethereum transactions in a +// given block. +func (b *Backend) GetBlockTransactionCount(block *tmrpctypes.ResultBlock) *hexutil.Uint { + blockRes, err := b.TendermintBlockResultByNumber(&block.Block.Height) + if err != nil { + return nil + } + + ethMsgs := b.EthMsgsFromTendermintBlock(block, blockRes) + n := hexutil.Uint(len(ethMsgs)) + return &n +} + +// TendermintBlockByNumber returns a Tendermint-formatted block for a given +// block number +func (b *Backend) TendermintBlockByNumber(blockNum rpc.BlockNumber) (*tmrpctypes.ResultBlock, error) { + height := blockNum.Int64() + if height <= 0 { + // fetch the latest block number from the app state, more accurate than the tendermint block store state. + n, err := b.BlockNumber() + if err != nil { + return nil, err + } + height = int64(n) //#nosec G701 -- checked for int overflow already + } + resBlock, err := b.clientCtx.Client.Block(b.ctx, &height) + if err != nil { + b.logger.Debug("tendermint client failed to get block", "height", height, "error", err.Error()) + return nil, err + } + + if resBlock.Block == nil { + b.logger.Debug("TendermintBlockByNumber block not found", "height", height) + return nil, nil + } + + return resBlock, nil +} + +// TendermintBlockResultByNumber returns a Tendermint-formatted block result +// by block number +func (b *Backend) TendermintBlockResultByNumber(height *int64) (*tmrpctypes.ResultBlockResults, error) { + sc, ok := b.clientCtx.Client.(tmrpcclient.SignClient) + if !ok { + return nil, errors.New("invalid rpc client") + } + return sc.BlockResults(b.ctx, height) +} + +// TendermintBlockByHash returns a Tendermint-formatted block by block number +func (b *Backend) TendermintBlockByHash(blockHash gethcommon.Hash) (*tmrpctypes.ResultBlock, error) { + sc, ok := b.clientCtx.Client.(tmrpcclient.SignClient) + if !ok { + return nil, errors.New("invalid rpc client") + } + resBlock, err := sc.BlockByHash(b.ctx, blockHash.Bytes()) + if err != nil { + b.logger.Debug("tendermint client failed to get block", "blockHash", blockHash.Hex(), "error", err.Error()) + return nil, err + } + + if resBlock == nil || resBlock.Block == nil { + b.logger.Debug("TendermintBlockByHash block not found", "blockHash", blockHash.Hex()) + return nil, nil + } + + return resBlock, nil +} + +// BlockNumberFromTendermint returns the BlockNumber from BlockNumberOrHash +func (b *Backend) BlockNumberFromTendermint(blockNrOrHash rpc.BlockNumberOrHash) (rpc.BlockNumber, error) { + switch { + case blockNrOrHash.BlockHash == nil && blockNrOrHash.BlockNumber == nil: + return rpc.EthEarliestBlockNumber, fmt.Errorf("types BlockHash and BlockNumber cannot be both nil") + case blockNrOrHash.BlockHash != nil: + blockNumber, err := b.BlockNumberFromTendermintByHash(*blockNrOrHash.BlockHash) + if err != nil { + return rpc.EthEarliestBlockNumber, err + } + return rpc.NewBlockNumber(blockNumber), nil + case blockNrOrHash.BlockNumber != nil: + return *blockNrOrHash.BlockNumber, nil + default: + return rpc.EthEarliestBlockNumber, nil + } +} + +// BlockNumberFromTendermintByHash returns the block height of given block hash +func (b *Backend) BlockNumberFromTendermintByHash(blockHash gethcommon.Hash) (*big.Int, error) { + resBlock, err := b.TendermintBlockByHash(blockHash) + if err != nil { + return nil, err + } + if resBlock == nil { + return nil, errors.Errorf("block not found for hash %s", blockHash.Hex()) + } + return big.NewInt(resBlock.Block.Height), nil +} + +// EthMsgsFromTendermintBlock returns all real MsgEthereumTxs from a +// Tendermint block. It also ensures consistency over the correct txs indexes +// across RPC endpoints +func (b *Backend) EthMsgsFromTendermintBlock( + resBlock *tmrpctypes.ResultBlock, + blockRes *tmrpctypes.ResultBlockResults, +) []*evm.MsgEthereumTx { + var result []*evm.MsgEthereumTx + block := resBlock.Block + + txResults := blockRes.TxsResults + + for i, tx := range block.Txs { + // Check if tx exists on EVM by cross checking with blockResults: + // - Include unsuccessful tx that exceeds block gas limit + // - Include unsuccessful tx that failed when committing changes to stateDB + // - Exclude unsuccessful tx with any other error but ExceedBlockGasLimit + isValidEnough, reason := rpc.TxIsValidEnough(txResults[i]) + if !isValidEnough { + b.logger.Debug( + "invalid tx result code", + "tm_tx_hash", eth.TmTxHashToString(tx.Hash()), + "reason", reason, + ) + continue + } + + tx, err := b.clientCtx.TxConfig.TxDecoder()(tx) + if err != nil { + b.logger.Debug("failed to decode transaction in block", "height", block.Height, "error", err.Error()) + continue + } + + for _, msg := range tx.GetMsgs() { + ethMsg, ok := msg.(*evm.MsgEthereumTx) + if !ok { + continue + } + + ethMsg.Hash = ethMsg.AsTransaction().Hash().Hex() + result = append(result, ethMsg) + } + } + return result +} + +// HeaderByNumber returns the block header identified by height. +func (b *Backend) HeaderByNumber(blockNum rpc.BlockNumber) (*gethcore.Header, error) { + resBlock, err := b.TendermintBlockByNumber(blockNum) + if err != nil { + return nil, err + } + + if resBlock == nil { + return nil, errors.Errorf("block not found for height %d", blockNum) + } + + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + return nil, fmt.Errorf("block result not found for height %d. %w", resBlock.Block.Height, err) + } + + bloom, err := b.BlockBloom(blockRes) + if err != nil { + b.logger.Debug("HeaderByNumber BlockBloom failed", "height", resBlock.Block.Height) + } + + baseFeeWei, err := b.BaseFeeWei(blockRes) + if err != nil { + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", resBlock.Block.Height, "error", err) + } + + ethHeader := rpc.EthHeaderFromTendermint(resBlock.Block.Header, bloom, baseFeeWei) + return ethHeader, nil +} + +// BlockBloom query block bloom filter from block results +func (b *Backend) BlockBloom(blockRes *tmrpctypes.ResultBlockResults) (gethcore.Bloom, error) { + msgType := proto.MessageName((*evm.EventBlockBloom)(nil)) + for _, event := range blockRes.EndBlockEvents { + if event.Type != msgType { + continue + } + blockBloomEvent, err := evm.EventBlockBloomFromABCIEvent(event) + if err != nil { + continue + } + return gethcore.BytesToBloom(hexutils.HexToBytes(blockBloomEvent.Bloom)), nil + } + return gethcore.Bloom{}, errors.New(msgType + " not found in end block results") +} + +// RPCBlockFromTendermintBlock returns a JSON-RPC compatible Ethereum block from a +// given Tendermint block and its block result. +func (b *Backend) RPCBlockFromTendermintBlock( + resBlock *tmrpctypes.ResultBlock, + blockRes *tmrpctypes.ResultBlockResults, + fullTx bool, +) (map[string]any, error) { + ethRPCTxs := []any{} + block := resBlock.Block + + baseFeeWei, err := b.BaseFeeWei(blockRes) + if err != nil { + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", block.Height, "error", err) + } + + msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) + for txIndex, ethMsg := range msgs { + if !fullTx { + hash := gethcommon.HexToHash(ethMsg.Hash) + ethRPCTxs = append(ethRPCTxs, hash) + continue + } + + tx := ethMsg.AsTransaction() + height := uint64(block.Height) //#nosec G701 -- checked for int overflow already + index := uint64(txIndex) //#nosec G701 -- checked for int overflow already + rpcTx, err := rpc.NewRPCTxFromEthTx( + tx, + gethcommon.BytesToHash(block.Hash()), + height, + index, + baseFeeWei, + b.chainID, + ) + if err != nil { + b.logger.Debug("NewTransactionFromData for receipt failed", "hash", tx.Hash().Hex(), "error", err.Error()) + continue + } + ethRPCTxs = append(ethRPCTxs, rpcTx) + } + + bloom, err := b.BlockBloom(blockRes) + if err != nil { + b.logger.Debug("failed to query BlockBloom", "height", block.Height, "error", err.Error()) + } + + req := &evm.QueryValidatorAccountRequest{ + ConsAddress: sdk.ConsAddress(block.Header.ProposerAddress).String(), + } + + var validatorAccAddr sdk.AccAddress + + ctx := rpc.NewContextWithHeight(block.Height) + res, err := b.queryClient.ValidatorAccount(ctx, req) + if err != nil { + b.logger.Debug( + "failed to query validator operator address", + "height", block.Height, + "cons-address", req.ConsAddress, + "error", err.Error(), + ) + // use zero address as the validator operator address + validatorAccAddr = sdk.AccAddress(gethcommon.Address{}.Bytes()) + } else { + validatorAccAddr, err = sdk.AccAddressFromBech32(res.AccountAddress) + if err != nil { + return nil, err + } + } + + validatorAddr := gethcommon.BytesToAddress(validatorAccAddr) + + gasLimit, err := rpc.BlockMaxGasFromConsensusParams(ctx, b.clientCtx, block.Height) + if err != nil { + b.logger.Error("failed to query consensus params", "error", err.Error()) + } + + gasUsed := uint64(0) + + for _, txsResult := range blockRes.TxsResults { + // workaround for cosmos-sdk bug. https://github.com/cosmos/cosmos-sdk/issues/10832 + if ShouldIgnoreGasUsed(txsResult) { + // block gas limit has exceeded, other txs must have failed with same reason. + break + } + gasUsed += uint64(txsResult.GetGasUsed()) // #nosec G701 -- checked for int overflow already + } + + formattedBlock := rpc.FormatBlock( + block.Header, block.Size(), + gasLimit, new(big.Int).SetUint64(gasUsed), + ethRPCTxs, bloom, validatorAddr, baseFeeWei, + ) + return formattedBlock, nil +} + +// EthBlockByNumber returns the Ethereum Block identified by number. +func (b *Backend) EthBlockByNumber(blockNum rpc.BlockNumber) (*gethcore.Block, error) { + resBlock, err := b.TendermintBlockByNumber(blockNum) + if err != nil { + return nil, err + } + if resBlock == nil { + // block not found + return nil, fmt.Errorf("block not found for height %d", blockNum) + } + + blockRes, err := b.TendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + return nil, fmt.Errorf("block result not found for height %d", resBlock.Block.Height) + } + + return b.EthBlockFromTendermintBlock(resBlock, blockRes) +} + +// EthBlockFromTendermintBlock returns an Ethereum Block type from Tendermint block +// EthBlockFromTendermintBlock +func (b *Backend) EthBlockFromTendermintBlock( + resBlock *tmrpctypes.ResultBlock, + blockRes *tmrpctypes.ResultBlockResults, +) (*gethcore.Block, error) { + block := resBlock.Block + height := block.Height + bloom, err := b.BlockBloom(blockRes) + if err != nil { + b.logger.Debug("HeaderByNumber BlockBloom failed", "height", height) + } + + baseFeeWei, err := b.BaseFeeWei(blockRes) + if err != nil { + // handle error for pruned node and log + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", height, "error", err) + } + + ethHeader := rpc.EthHeaderFromTendermint(block.Header, bloom, baseFeeWei) + msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) + + txs := make([]*gethcore.Transaction, len(msgs)) + for i, ethMsg := range msgs { + txs[i] = ethMsg.AsTransaction() + } + + // TODO: add tx receipts + ethBlock := gethcore.NewBlock(ethHeader, txs, nil, nil, trie.NewStackTrie(nil)) + return ethBlock, nil +} diff --git a/eth/rpc/backend/blocks_test.go b/eth/rpc/backend/blocks_test.go new file mode 100644 index 000000000..918bb8794 --- /dev/null +++ b/eth/rpc/backend/blocks_test.go @@ -0,0 +1,111 @@ +package backend_test + +import ( + "github.com/ethereum/go-ethereum/common/hexutil" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" +) + +func (s *BackendSuite) TestBlockNumber() { + blockHeight, err := s.backend.BlockNumber() + s.Require().NoError(err) + blockHeightU64, err := hexutil.DecodeUint64(blockHeight.String()) + s.NoError(err) + s.Greater(blockHeightU64, uint64(1)) + + latestHeight, _ := s.network.LatestHeight() + resp, err := s.backend.BlockNumber() + s.Require().NoError(err, resp) + // Rather than checking exact equality, which might not be true due to + // latency. Add a cushion of 2 blocks. + s.Require().LessOrEqual(uint64(latestHeight)-uint64(blockHeight), uint64(2)) +} + +func (s *BackendSuite) TestGetBlockByNumberr() { + block, err := s.backend.GetBlockByNumber(transferTxBlockNumber, true) + s.Require().NoError(err) + s.Require().NotNil(block) + s.Require().Greater(len(block["transactions"].([]interface{})), 0) + s.Require().NotNil(block["size"]) + s.Require().NotNil(block["nonce"]) + s.Require().Equal(int64(block["number"].(hexutil.Uint64)), transferTxBlockNumber.Int64()) +} + +func (s *BackendSuite) TestGetBlockByHash() { + blockMap, err := s.backend.GetBlockByHash(transferTxBlockHash, true) + s.Require().NoError(err) + AssertBlockContents(s, blockMap) +} + +func (s *BackendSuite) TestBlockNumberFromTendermint() { + testCases := []struct { + name string + blockNrOrHash rpc.BlockNumberOrHash + wantBlockNumber rpc.BlockNumber + wantErr string + }{ + { + name: "happy: block number specified", + blockNrOrHash: rpc.BlockNumberOrHash{ + BlockNumber: &transferTxBlockNumber, + }, + wantBlockNumber: transferTxBlockNumber, + wantErr: "", + }, + { + name: "happy: block hash specified", + blockNrOrHash: rpc.BlockNumberOrHash{ + BlockHash: &transferTxBlockHash, + }, + wantBlockNumber: transferTxBlockNumber, + wantErr: "", + }, + { + name: "sad: neither block number nor hash specified", + blockNrOrHash: rpc.BlockNumberOrHash{}, + wantBlockNumber: 0, + wantErr: "BlockHash and BlockNumber cannot be both nil", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + blockNumber, err := s.backend.BlockNumberFromTendermint(tc.blockNrOrHash) + + if tc.wantErr != "" { + s.Require().Error(err) + return + } + s.Require().NoError(err) + s.Require().Equal(tc.wantBlockNumber, blockNumber) + }) + } +} + +func (s *BackendSuite) TestEthBlockByNumber() { + block, err := s.backend.EthBlockByNumber(transferTxBlockNumber) + s.Require().NoError(err) + s.Require().NotNil(block) + s.Require().Equal(transferTxBlockNumber.Int64(), block.Number().Int64()) + s.Require().Greater(block.Transactions().Len(), 0) + s.Require().NotNil(block.ParentHash()) + s.Require().NotNil(block.UncleHash()) +} + +func (s *BackendSuite) TestGetBlockTransactionCountByHash() { + txCount := s.backend.GetBlockTransactionCountByHash(transferTxBlockHash) + s.Require().Greater((uint64)(*txCount), uint64(0)) +} + +func (s *BackendSuite) TestGetBlockTransactionCountByNumber() { + txCount := s.backend.GetBlockTransactionCountByNumber(transferTxBlockNumber) + s.Require().Greater((uint64)(*txCount), uint64(0)) +} + +func AssertBlockContents(s *BackendSuite, blockMap map[string]interface{}) { + s.Require().NotNil(blockMap) + s.Require().Greater(len(blockMap["transactions"].([]interface{})), 0) + s.Require().NotNil(blockMap["size"]) + s.Require().NotNil(blockMap["nonce"]) + s.Require().Equal(int64(blockMap["number"].(hexutil.Uint64)), transferTxBlockNumber.Int64()) +} diff --git a/eth/rpc/backend/call_tx.go b/eth/rpc/backend/call_tx.go new file mode 100644 index 000000000..19b9ee4fc --- /dev/null +++ b/eth/rpc/backend/call_tx.go @@ -0,0 +1,345 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "math/big" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// SendRawTransaction send a raw Ethereum transaction. +func (b *Backend) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) { + // RLP decode raw transaction bytes + tx := &gethcore.Transaction{} + if err := tx.UnmarshalBinary(data); err != nil { + b.logger.Error("transaction decoding failed", "error", err.Error()) + return common.Hash{}, err + } + + // check the local node config in case unprotected txs are disabled + if !b.allowUnprotectedTxs && !tx.Protected() { + // Ensure only eip155 signed transactions are submitted if EIP155Required is set. + return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC") + } + + ethereumTx := &evm.MsgEthereumTx{} + if err := ethereumTx.FromEthereumTx(tx); err != nil { + b.logger.Error("transaction converting failed", "error", err.Error()) + return common.Hash{}, err + } + + if err := ethereumTx.ValidateBasic(); err != nil { + b.logger.Debug("tx failed basic validation", "error", err.Error()) + return common.Hash{}, err + } + + cosmosTx, err := ethereumTx.BuildTx(b.clientCtx.TxConfig.NewTxBuilder(), evm.EVMBankDenom) + if err != nil { + b.logger.Error("failed to build cosmos tx", "error", err.Error()) + return common.Hash{}, err + } + + // Encode transaction by default Tx encoder + txBytes, err := b.clientCtx.TxConfig.TxEncoder()(cosmosTx) + if err != nil { + b.logger.Error("failed to encode eth tx using default encoder", "error", err.Error()) + return common.Hash{}, err + } + + txHash := ethereumTx.AsTransaction().Hash() + b.logger.Debug("eth_sendRawTransaction", + "txHash", txHash.Hex(), + ) + + syncCtx := b.clientCtx.WithBroadcastMode(flags.BroadcastSync) + rsp, err := syncCtx.BroadcastTx(txBytes) + if rsp != nil && rsp.Code != 0 { + err = errorsmod.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog) + } + if err != nil { + b.logger.Error("failed to broadcast tx", "error", err.Error()) + return txHash, err + } + b.logger.Debug("eth_sendRawTransaction", + "blockHeight", fmt.Sprintf("%d", rsp.Height), + ) + + return txHash, nil +} + +// SetTxDefaults populates tx message with default values in case they are not +// provided on the args +func (b *Backend) SetTxDefaults(args evm.JsonTxArgs) (evm.JsonTxArgs, error) { + if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { + return args, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + + head, _ := b.CurrentHeader() // #nosec G703 -- no need to check error cause we're already checking that head == nil + if head == nil { + return args, errors.New("latest header is nil") + } + + // If user specifies both maxPriorityfee and maxFee, then we do not + // need to consult the chain for defaults. + if args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil { + // In this clause, user left some fields unspecified. + if head.BaseFee != nil && args.GasPrice == nil { + if args.MaxPriorityFeePerGas == nil { + tip, err := b.SuggestGasTipCap(head.BaseFee) + if err != nil { + return args, err + } + args.MaxPriorityFeePerGas = (*hexutil.Big)(tip) + } + + if args.MaxFeePerGas == nil { + gasFeeCap := new(big.Int).Add( + (*big.Int)(args.MaxPriorityFeePerGas), + new(big.Int).Mul(head.BaseFee, big.NewInt(2)), + ) + args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) + } + + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return args, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } + } else { + if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil { + return args, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + } + + if args.GasPrice == nil { + price, err := b.SuggestGasTipCap(head.BaseFee) + if err != nil { + return args, err + } + if head.BaseFee != nil { + // The legacy tx gas price suggestion should not add 2x base fee + // because all fees are consumed, so it would result in a spiral + // upwards. + price.Add(price, head.BaseFee) + } + args.GasPrice = (*hexutil.Big)(price) + } + } + } else { + // Both maxPriorityfee and maxFee set by caller. Sanity-check their internal relation + if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { + return args, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) + } + } + + if args.Value == nil { + args.Value = new(hexutil.Big) + } + if args.Nonce == nil && args.From != nil { + // get the nonce from the account retriever + // ignore error in case the account doesn't exist yet + nonce, _ := b.getAccountNonce(*args.From, true, 0, b.logger) // #nosec G703s + args.Nonce = (*hexutil.Uint64)(&nonce) + } + + if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { + return args, errors.New("both 'data' and 'input' are set and not equal. Please use 'input' to pass transaction call data") + } + + if args.To == nil { + // Contract creation + var input []byte + if args.Data != nil { + input = *args.Data + } else if args.Input != nil { + input = *args.Input + } + + if len(input) == 0 { + return args, errors.New("contract creation without any data provided") + } + } + + if args.Gas == nil { + // For backwards-compatibility reason, we try both input and data + // but input is preferred. + input := args.Input + if input == nil { + input = args.Data + } + + callArgs := evm.JsonTxArgs{ + From: args.From, + To: args.To, + Gas: args.Gas, + GasPrice: args.GasPrice, + MaxFeePerGas: args.MaxFeePerGas, + MaxPriorityFeePerGas: args.MaxPriorityFeePerGas, + Value: args.Value, + Data: input, + AccessList: args.AccessList, + ChainID: args.ChainID, + Nonce: args.Nonce, + } + + blockNr := rpc.EthPendingBlockNumber + estimated, err := b.EstimateGas(callArgs, &blockNr) + if err != nil { + return args, err + } + args.Gas = &estimated + b.logger.Debug("estimate gas usage automatically", "gas", args.Gas) + } + + if args.ChainID == nil { + args.ChainID = (*hexutil.Big)(b.chainID) + } + + return args, nil +} + +// EstimateGas returns an estimate of gas usage for the given smart contract call. +func (b *Backend) EstimateGas( + args evm.JsonTxArgs, blockNrOptional *rpc.BlockNumber, +) (hexutil.Uint64, error) { + blockNr := rpc.EthPendingBlockNumber + if blockNrOptional != nil { + blockNr = *blockNrOptional + } + + bz, err := json.Marshal(&args) + if err != nil { + return 0, err + } + + header, err := b.TendermintBlockByNumber(blockNr) + if err != nil { + // the error message imitates geth behavior + return 0, errors.New("header not found") + } + + req := evm.EthCallRequest{ + Args: bz, + GasCap: b.RPCGasCap(), + ProposerAddress: sdk.ConsAddress(header.Block.ProposerAddress), + ChainId: b.chainID.Int64(), + } + + // From ContextWithHeight: if the provided height is 0, + // it will return an empty context and the gRPC query will use + // the latest block height for querying. + res, err := b.queryClient.EstimateGas(rpc.NewContextWithHeight(blockNr.Int64()), &req) + if err != nil { + return 0, err + } + return hexutil.Uint64(res.Gas), nil +} + +// DoCall performs a simulated call operation through the evmtypes. It returns the +// estimated gas used on the operation or an error if fails. +func (b *Backend) DoCall( + args evm.JsonTxArgs, blockNr rpc.BlockNumber, +) (*evm.MsgEthereumTxResponse, error) { + bz, err := json.Marshal(&args) + if err != nil { + return nil, err + } + header, err := b.TendermintBlockByNumber(blockNr) + if err != nil { + // the error message imitates geth behavior + return nil, errors.New("header not found") + } + + req := evm.EthCallRequest{ + Args: bz, + GasCap: b.RPCGasCap(), + ProposerAddress: sdk.ConsAddress(header.Block.ProposerAddress), + ChainId: b.chainID.Int64(), + } + + // From ContextWithHeight: if the provided height is 0, + // it will return an empty context and the gRPC query will use + // the latest block height for querying. + ctx := rpc.NewContextWithHeight(blockNr.Int64()) + timeout := b.RPCEVMTimeout() + + // Setup context so it may be canceled the call has completed + // or, in case of unmetered gas, setup a context with a timeout. + var cancel context.CancelFunc + if timeout > 0 { + ctx, cancel = context.WithTimeout(ctx, timeout) + } else { + ctx, cancel = context.WithCancel(ctx) + } + + // Make sure the context is canceled when the call has completed + // this makes sure resources are cleaned up. + defer cancel() + + res, err := b.queryClient.EthCall(ctx, &req) + if err != nil { + return nil, err + } + + if res.Failed() { + if res.VmError == vm.ErrExecutionReverted.Error() { + return nil, evm.NewRevertError(res.Ret) + } + return nil, status.Error(codes.Internal, res.VmError) + } + + return res, nil +} + +// GasPrice returns the current gas price based on Ethermint's gas price oracle. +func (b *Backend) GasPrice() (*hexutil.Big, error) { + var ( + result *big.Int + err error + ) + + head, err := b.CurrentHeader() + if err != nil { + return nil, err + } + + if head.BaseFee != nil { + result, err = b.SuggestGasTipCap(head.BaseFee) + if err != nil { + return nil, err + } + result = result.Add(result, head.BaseFee) + } else { + result = big.NewInt(b.RPCMinGasPrice()) + } + + // return at least GlobalMinGasPrice + minGasPrice, err := b.GlobalMinGasPrice() + if err != nil { + return nil, err + } + minGasPriceInt := minGasPrice + if result.Cmp(minGasPriceInt) < 0 { + result = minGasPriceInt + } + + return (*hexutil.Big)(result), nil +} + +func (b *Backend) ClientCtx() client.Context { + return b.clientCtx +} diff --git a/eth/rpc/backend/call_tx_test.go b/eth/rpc/backend/call_tx_test.go new file mode 100644 index 000000000..3267d8fd9 --- /dev/null +++ b/eth/rpc/backend/call_tx_test.go @@ -0,0 +1,90 @@ +package backend_test + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +func (s *BackendSuite) TestSetTxDefaults() { + testCases := []struct { + name string + jsonTxArgs evm.JsonTxArgs + wantErr string + }{ + { + name: "happy: minimal args set", + jsonTxArgs: evm.JsonTxArgs{ + From: &s.fundedAccEthAddr, + To: &recipient, + Value: (*hexutil.Big)(evm.NativeToWei(big.NewInt(1))), + }, + wantErr: "", + }, + { + name: "happy: gas price set", + jsonTxArgs: evm.JsonTxArgs{ + From: &s.fundedAccEthAddr, + To: &recipient, + GasPrice: (*hexutil.Big)(evm.NativeToWei(big.NewInt(1))), + Value: (*hexutil.Big)(evm.NativeToWei(big.NewInt(1))), + }, + wantErr: "", + }, + { + name: "sad: no to (contract creation) and no data", + jsonTxArgs: evm.JsonTxArgs{ + From: &s.fundedAccEthAddr, + }, + wantErr: "contract creation without any data provided", + }, + { + name: "sad: transfer without from specified generates new empty account", + jsonTxArgs: evm.JsonTxArgs{ + To: &recipient, + Value: (*hexutil.Big)(evm.NativeToWei(big.NewInt(1))), + }, + wantErr: "insufficient balance", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + jsonTxArgs, err := s.backend.SetTxDefaults(tc.jsonTxArgs) + + if tc.wantErr != "" { + s.Require().Error(err) + return + } + s.Require().NoError(err) + s.Require().NotNil(jsonTxArgs.Nonce) + s.Require().NotNil(jsonTxArgs.Gas) + s.Require().Greater(*jsonTxArgs.Nonce, hexutil.Uint64(0)) + s.Require().Greater(*jsonTxArgs.Gas, hexutil.Uint64(0)) + s.Require().Equal(jsonTxArgs.ChainID.ToInt().Int64(), appconst.ETH_CHAIN_ID_DEFAULT) + }) + } +} + +func (s *BackendSuite) TestDoCall() { + jsonTxArgs := evm.JsonTxArgs{ + From: &s.fundedAccEthAddr, + To: &recipient, + Value: (*hexutil.Big)(evm.NativeToWei(big.NewInt(1))), + } + txResponse, err := s.backend.DoCall(jsonTxArgs, rpc.EthPendingBlockNumber) + s.Require().NoError(err) + s.Require().NotNil(txResponse) + s.Require().Greater(txResponse.GasUsed, uint64(0)) +} + +func (s *BackendSuite) TestGasPrice() { + gasPrice, err := s.backend.GasPrice() + s.Require().NoError(err) + s.Require().NotNil(gasPrice) + s.Require().Greater(gasPrice.ToInt().Int64(), int64(0)) +} diff --git a/eth/rpc/backend/chain_info.go b/eth/rpc/backend/chain_info.go new file mode 100644 index 000000000..ae1bdd270 --- /dev/null +++ b/eth/rpc/backend/chain_info.go @@ -0,0 +1,189 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "fmt" + "math/big" + + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + gethrpc "github.com/ethereum/go-ethereum/rpc" + "github.com/pkg/errors" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// ChainID is the EIP-155 replay-protection chain id for the current ethereum chain config. +func (b *Backend) ChainID() *hexutil.Big { + return (*hexutil.Big)(eth.ParseEthChainID(b.clientCtx.ChainID)) +} + +// ChainConfig returns the latest ethereum chain configuration +func (b *Backend) ChainConfig() *params.ChainConfig { + _, err := b.queryClient.Params(b.ctx, &evm.QueryParamsRequest{}) + if err != nil { + return nil + } + return evm.EthereumConfig(b.chainID) +} + +// BaseFeeWei returns the EIP-1559 base fee. +// If the base fee is not enabled globally, the query returns nil. +func (b *Backend) BaseFeeWei( + blockRes *tmrpctypes.ResultBlockResults, +) (baseFeeWei *big.Int, err error) { + res, err := b.queryClient.BaseFee(rpc.NewContextWithHeight(blockRes.Height), &evm.QueryBaseFeeRequest{}) + if err != nil || res.BaseFee == nil { + return nil, nil + } + return res.BaseFee.BigInt(), nil +} + +// CurrentHeader returns the latest block header +// This will return error as per node configuration +// if the ABCI responses are discarded ('discard_abci_responses' config param) +func (b *Backend) CurrentHeader() (*gethcore.Header, error) { + return b.HeaderByNumber(rpc.EthLatestBlockNumber) +} + +// PendingTransactions returns the transactions that are in the transaction pool +// and have a from address that is one of the accounts this node manages. +func (b *Backend) PendingTransactions() ([]*sdk.Tx, error) { + mc, ok := b.clientCtx.Client.(tmrpcclient.MempoolClient) + if !ok { + return nil, errors.New("invalid rpc client") + } + + res, err := mc.UnconfirmedTxs(b.ctx, nil) + if err != nil { + return nil, err + } + + result := make([]*sdk.Tx, 0, len(res.Txs)) + for _, txBz := range res.Txs { + tx, err := b.clientCtx.TxConfig.TxDecoder()(txBz) + if err != nil { + return nil, err + } + result = append(result, &tx) + } + + return result, nil +} + +// FeeHistory returns data relevant for fee estimation based on the specified range of blocks. +func (b *Backend) FeeHistory( + userBlockCount gethrpc.DecimalOrHex, // number blocks to fetch, maximum is 100 + lastBlock gethrpc.BlockNumber, // the block to start search, to oldest + rewardPercentiles []float64, // percentiles to fetch reward +) (*rpc.FeeHistoryResult, error) { + blockEnd := int64(lastBlock) //#nosec G701 -- checked for int overflow already + + if blockEnd < 0 { + blockNumber, err := b.BlockNumber() + if err != nil { + return nil, err + } + blockEnd = int64(blockNumber) //#nosec G701 -- checked for int overflow already + } + + blocks := int64(userBlockCount) // #nosec G701 -- checked for int overflow already + maxBlockCount := int64(b.cfg.JSONRPC.FeeHistoryCap) // #nosec G701 -- checked for int overflow already + if blocks > maxBlockCount { + return nil, fmt.Errorf("FeeHistory user block count %d higher than %d", blocks, maxBlockCount) + } + + if blockEnd+1 < blocks { + blocks = blockEnd + 1 + } + // Ensure not trying to retrieve before genesis. + blockStart := blockEnd + 1 - blocks + oldestBlock := (*hexutil.Big)(big.NewInt(blockStart)) + + // prepare space + reward := make([][]*hexutil.Big, blocks) + rewardCount := len(rewardPercentiles) + for i := 0; i < int(blocks); i++ { + reward[i] = make([]*hexutil.Big, rewardCount) + } + + thisBaseFee := make([]*hexutil.Big, blocks+1) + thisGasUsedRatio := make([]float64, blocks) + + // rewards should only be calculated if reward percentiles were included + calculateRewards := rewardCount != 0 + + // fetch block + for blockID := blockStart; blockID <= blockEnd; blockID++ { + index := int32(blockID - blockStart) // #nosec G701 + // tendermint block + tendermintblock, err := b.TendermintBlockByNumber(rpc.BlockNumber(blockID)) + if tendermintblock == nil { + return nil, err + } + + // eth block + ethBlock, err := b.GetBlockByNumber(rpc.BlockNumber(blockID), true) + if ethBlock == nil { + return nil, err + } + + // tendermint block result + tendermintBlockResult, err := b.TendermintBlockResultByNumber(&tendermintblock.Block.Height) + if tendermintBlockResult == nil { + b.logger.Debug("block result not found", "height", tendermintblock.Block.Height, "error", err.Error()) + return nil, err + } + + oneFeeHistory := rpc.OneFeeHistory{} + err = b.retrieveEVMTxFeesFromBlock(tendermintblock, ðBlock, rewardPercentiles, tendermintBlockResult, &oneFeeHistory) + if err != nil { + return nil, err + } + + // copy + thisBaseFee[index] = (*hexutil.Big)(oneFeeHistory.BaseFee) + thisBaseFee[index+1] = (*hexutil.Big)(oneFeeHistory.NextBaseFee) + thisGasUsedRatio[index] = oneFeeHistory.GasUsedRatio + if calculateRewards { + for j := 0; j < rewardCount; j++ { + reward[index][j] = (*hexutil.Big)(oneFeeHistory.Reward[j]) + if reward[index][j] == nil { + reward[index][j] = (*hexutil.Big)(big.NewInt(0)) + } + } + } + } + + feeHistory := rpc.FeeHistoryResult{ + OldestBlock: oldestBlock, + BaseFee: thisBaseFee, + GasUsedRatio: thisGasUsedRatio, + } + + if calculateRewards { + feeHistory.Reward = reward + } + + return &feeHistory, nil +} + +// SuggestGasTipCap Not yet supported. Returns 0 as the suggested tip cap. +// After implementing tx prioritization, this function can come to life. +func (b *Backend) SuggestGasTipCap(baseFee *big.Int) (*big.Int, error) { + maxDelta := big.NewInt(0) + return maxDelta, nil +} + +// GlobalMinGasPrice returns the minimum gas price for all nodes. +// This is distinct from the individual configuration set by the validator set. +func (b *Backend) GlobalMinGasPrice() (*big.Int, error) { + // TODO: feat(eth): dynamic fees + return big.NewInt(0), nil +} diff --git a/eth/rpc/backend/chain_info_test.go b/eth/rpc/backend/chain_info_test.go new file mode 100644 index 000000000..5056b5637 --- /dev/null +++ b/eth/rpc/backend/chain_info_test.go @@ -0,0 +1,93 @@ +package backend_test + +import ( + "math/big" + + gethrpc "github.com/ethereum/go-ethereum/rpc" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *BackendSuite) TestChainID() { + s.Require().Equal(appconst.ETH_CHAIN_ID_DEFAULT, s.backend.ChainID().ToInt().Int64()) +} + +func (s *BackendSuite) TestChainConfig() { + config := s.backend.ChainConfig() + s.Require().Equal(appconst.ETH_CHAIN_ID_DEFAULT, config.ChainID.Int64()) + s.Require().Equal(int64(0), config.LondonBlock.Int64()) +} + +func (s *BackendSuite) TestBaseFeeWei() { + resBlock, err := s.backend.TendermintBlockResultByNumber(transferTxBlockNumber.TmHeight()) + s.Require().NoError(err) + baseFeeWei, err := s.backend.BaseFeeWei(resBlock) + s.Require().NoError(err) + s.Require().Equal(evm.BASE_FEE_WEI, baseFeeWei) +} + +func (s *BackendSuite) TestCurrentHeader() { + currentHeader, err := s.backend.CurrentHeader() + s.Require().NoError(err) + s.Require().NotNil(currentHeader) + s.Require().GreaterOrEqual(currentHeader.Number.Int64(), transferTxBlockNumber.Int64()) +} + +func (s *BackendSuite) TestPendingTransactions() { + // Create pending tx: don't wait for next block + randomEthAddr := evmtest.NewEthPrivAcc().EthAddr + txHash := s.SendNibiViaEthTransfer(randomEthAddr, big.NewInt(123), false) + txs, err := s.backend.PendingTransactions() + s.Require().NoError(err) + s.Require().NotNil(txs) + s.Require().NotNil(txHash) + s.Require().Greater(len(txs), 0) + txFound := false + for _, tx := range txs { + msg, err := evm.UnwrapEthereumMsg(tx, txHash) + if err != nil { + // not ethereum tx + continue + } + if msg.Hash == txHash.String() { + txFound = true + } + } + s.Require().True(txFound, "pending tx not found") +} + +func (s *BackendSuite) TestFeeHistory() { + currentBlock, err := s.backend.BlockNumber() + s.Require().NoError(err) + blockCount := 2 // blocks to search backwards from the current block + percentiles := []float64{50, 100} + + res, err := s.backend.FeeHistory( + (gethrpc.DecimalOrHex)(blockCount), + gethrpc.BlockNumber(int64(currentBlock)), + percentiles, + ) + s.Require().NoError(err) + s.Require().NotNil(res) + s.Require().Len(res.Reward, blockCount) + s.Require().Len(res.BaseFee, blockCount+1) + s.Require().Len(res.GasUsedRatio, len(percentiles)) + + for _, gasUsed := range res.GasUsedRatio { + s.Require().LessOrEqual(gasUsed, float64(1)) + } +} + +func (s *BackendSuite) TestSuggestGasTipCap() { + tipCap, err := s.backend.SuggestGasTipCap(big.NewInt(1)) + s.Require().NoError(err) + s.Require().Equal(big.NewInt(0), tipCap) +} + +func (s *BackendSuite) TestGlobalMinGasPrice() { + gasPrice, err := s.backend.GlobalMinGasPrice() + s.Require().NoError(err) + s.Require().Equal(big.NewInt(0), gasPrice) +} diff --git a/eth/rpc/backend/client_test.go b/eth/rpc/backend/client_test.go new file mode 100644 index 000000000..f0dcea536 --- /dev/null +++ b/eth/rpc/backend/client_test.go @@ -0,0 +1 @@ +package backend_test diff --git a/eth/rpc/backend/gas_used_test.go b/eth/rpc/backend/gas_used_test.go new file mode 100644 index 000000000..8362d599f --- /dev/null +++ b/eth/rpc/backend/gas_used_test.go @@ -0,0 +1,226 @@ +package backend_test + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +// TestGasUsedTransfers verifies that gas used is correctly calculated for simple transfers. +// Test creates 2 eth transfer txs that are supposed to be included in the same block. +// It checks that gas used is the same for both txs and the total block gas is greater than the sum of 2 gas used. +func (s *BackendSuite) TestGasUsedTransfers() { + // Test is broadcasting txs. Lock to avoid nonce conflicts. + testMutex.Lock() + defer testMutex.Unlock() + + // Start with new block + s.Require().NoError(s.network.WaitForNextBlock()) + balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr) + + // Send 2 similar transfers + randomEthAddr := evmtest.NewEthPrivAcc().EthAddr + txHash1 := s.SendNibiViaEthTransfer(randomEthAddr, amountToSend, false) + txHash2 := s.SendNibiViaEthTransfer(randomEthAddr, amountToSend, false) + + blockNumber1, _, receipt1 := WaitForReceipt(s, txHash1) + blockNumber2, _, receipt2 := WaitForReceipt(s, txHash2) + + s.Require().NotNil(receipt1) + s.Require().NotNil(receipt2) + + s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt1.Status) + s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt2.Status) + + // Expect txs are included into one block + s.Require().Equal(blockNumber1, blockNumber2) + + // Ensure that gas used is the same for both transactions + s.Require().Equal(receipt1.GasUsed, receipt2.GasUsed) + + // Get block receipt and check gas used + block, err := s.backend.GetBlockByNumber(rpc.NewBlockNumber(blockNumber1), false) + s.Require().NoError(err) + s.Require().NotNil(block) + s.Require().NotNil(block["gasUsed"]) + s.Require().GreaterOrEqual(block["gasUsed"].(*hexutil.Big).ToInt().Uint64(), receipt1.GasUsed+receipt2.GasUsed) + + // Balance after should be equal to balance before minus gas used and amount sent + balanceAfter := s.getUnibiBalance(s.fundedAccEthAddr) + s.Require().Equal( + receipt1.GasUsed+receipt2.GasUsed+2, + balanceBefore.Uint64()-balanceAfter.Uint64(), + ) +} + +// TestGasUsedFunTokens verifies that gas used is correctly calculated for precompile "sendToBank" txs. +// Test creates 3 txs: 2 successful and one failing. +// Successful txs gas should be refunded and failing tx should consume 100% of the gas limit. +// It also checks that txs are included in the same block and block gas is greater or equals +// to the total gas used by txs. +func (s *BackendSuite) TestGasUsedFunTokens() { + // Test is broadcasting txs. Lock to avoid nonce conflicts. + testMutex.Lock() + defer testMutex.Unlock() + + // Create funtoken from erc20 + erc20Addr, err := eth.NewEIP55AddrFromStr(testContractAddress.String()) + s.Require().NoError(err) + + nonce := s.getCurrentNonce(s.node.EthAddress) + balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr) + + txResp, err := s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ + Sender: s.node.Address.String(), + FromErc20: &erc20Addr, + }) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().NoError(s.network.WaitForNextBlock()) + + randomNibiAddress := testutil.AccAddress() + packedArgsPass, err := embeds.SmartContract_FunToken.ABI.Pack( + "sendToBank", + erc20Addr.Address, + big.NewInt(1), + randomNibiAddress.String(), + ) + s.Require().NoError(err) + + nonce = s.getCurrentNonce(s.fundedAccEthAddr) + txHash1 := SendTransaction( + s, + &gethcore.LegacyTx{ + Nonce: nonce, + To: &precompile.PrecompileAddr_FunToken, + Data: packedArgsPass, + Gas: 1_500_000, + GasPrice: big.NewInt(1), + }, + false, + ) + + packedArgsFail, err := embeds.SmartContract_FunToken.ABI.Pack( + "sendToBank", + erc20Addr.Address, + big.NewInt(1), + "invalidAddress", + ) + s.Require().NoError(err) + txHash2 := SendTransaction( // should fail due to invalid recipient address + s, + &gethcore.LegacyTx{ + Nonce: nonce + 1, + To: &precompile.PrecompileAddr_FunToken, + Data: packedArgsFail, + Gas: 1_500_000, + GasPrice: big.NewInt(1), + }, + false, + ) + txHash3 := SendTransaction( + s, + &gethcore.LegacyTx{ + Nonce: nonce + 2, + To: &precompile.PrecompileAddr_FunToken, + Data: packedArgsPass, + Gas: 1_500_000, + GasPrice: big.NewInt(1), + }, + false, + ) + blockNumber1, _, receipt1 := WaitForReceipt(s, txHash1) + blockNumber2, _, receipt2 := WaitForReceipt(s, txHash2) + blockNumber3, _, receipt3 := WaitForReceipt(s, txHash3) + + s.Require().NotNil(receipt1) + s.Require().NotNil(receipt2) + s.Require().NotNil(receipt3) + + s.Require().NotNil(blockNumber1) + s.Require().NotNil(blockNumber2) + s.Require().NotNil(blockNumber3) + + // 1 and 3 should pass and 2 should fail + s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt1.Status) + s.Require().Equal(gethcore.ReceiptStatusFailed, receipt2.Status) + s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt3.Status) + + // TX 1 and 3 should have gas used lower than specified gas limit + s.Require().Less(receipt1.GasUsed, uint64(500_000)) + s.Require().Less(receipt3.GasUsed, uint64(500_000)) + + // TX 2 should have gas used equal to specified gas limit as it failed + s.Require().Equal(uint64(1_500_000), receipt2.GasUsed) + + block, err := s.backend.GetBlockByNumber(rpc.NewBlockNumber(blockNumber1), false) + s.Require().NoError(err) + s.Require().NotNil(block) + s.Require().NotNil(block["gasUsed"]) + s.Require().GreaterOrEqual( + block["gasUsed"].(*hexutil.Big).ToInt().Uint64(), + receipt1.GasUsed+receipt2.GasUsed+receipt3.GasUsed, + ) + + // Balance after should be equal to balance before minus gas used + balanceAfter := s.getUnibiBalance(s.fundedAccEthAddr) + s.Require().Equal( + receipt1.GasUsed+receipt2.GasUsed+receipt3.GasUsed, + balanceBefore.Uint64()-balanceAfter.Uint64(), + ) +} + +// TestMultipleMsgsTxGasUsage tests that the gas is correctly consumed per message in a single transaction. +func (s *BackendSuite) TestMultipleMsgsTxGasUsage() { + // Test is broadcasting txs. Lock to avoid nonce conflicts. + testMutex.Lock() + defer testMutex.Unlock() + + balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr) + + nonce := s.getCurrentNonce(s.fundedAccEthAddr) + + contractCreationGasLimit := uint64(1_500_000) + contractCallGasLimit := uint64(100_000) + + // Create series of 3 tx messages. Expecting nonce to be incremented by 3 + creationTx := s.buildContractCreationTx(nonce, contractCreationGasLimit) + firstTransferTx := s.buildContractCallTx(testContractAddress, nonce+1, contractCallGasLimit) + secondTransferTx := s.buildContractCallTx(testContractAddress, nonce+2, contractCallGasLimit) + + // Create and broadcast SDK transaction + sdkTx := s.buildSDKTxWithEVMMessages( + creationTx, + firstTransferTx, + secondTransferTx, + ) + s.broadcastSDKTx(sdkTx) + + _, _, receiptContractCreation := WaitForReceipt(s, creationTx.Hash()) + _, _, receiptFirstTransfer := WaitForReceipt(s, firstTransferTx.Hash()) + _, _, receiptSecondTransfer := WaitForReceipt(s, secondTransferTx.Hash()) + + s.Require().Greater(receiptContractCreation.GasUsed, uint64(0)) + s.Require().LessOrEqual(receiptContractCreation.GasUsed, contractCreationGasLimit) + + s.Require().Greater(receiptFirstTransfer.GasUsed, uint64(0)) + s.Require().LessOrEqual(receiptFirstTransfer.GasUsed, contractCallGasLimit) + + s.Require().Greater(receiptSecondTransfer.GasUsed, uint64(0)) + s.Require().LessOrEqual(receiptSecondTransfer.GasUsed, contractCallGasLimit) + + balanceAfter := s.getUnibiBalance(s.fundedAccEthAddr) + s.Require().Equal( + receiptContractCreation.GasUsed+receiptFirstTransfer.GasUsed+receiptSecondTransfer.GasUsed, + balanceBefore.Uint64()-balanceAfter.Uint64(), + ) +} diff --git a/eth/rpc/backend/node_info.go b/eth/rpc/backend/node_info.go new file mode 100644 index 000000000..e95488b2a --- /dev/null +++ b/eth/rpc/backend/node_info.go @@ -0,0 +1,96 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "time" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// Accounts returns the list of accounts available to this node. +func (b *Backend) Accounts() ([]gethcommon.Address, error) { + addresses := make([]gethcommon.Address, 0) // return [] instead of nil if empty + + infos, err := b.clientCtx.Keyring.List() + if err != nil { + return addresses, err + } + + for _, info := range infos { + pubKey, err := info.GetPubKey() + if err != nil { + return nil, err + } + addressBytes := pubKey.Address().Bytes() + addresses = append(addresses, gethcommon.BytesToAddress(addressBytes)) + } + + return addresses, nil +} + +// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not +// yet received the latest block headers from its peers. In case it is synchronizing: +// - startingBlock: block number this node started to synchronize from +// - currentBlock: block number this node is currently importing +// - highestBlock: block number of the highest block header this node has received from peers +// - pulledStates: number of state entries processed until now +// - knownStates: number of known state entries that still need to be pulled +func (b *Backend) Syncing() (any, error) { + status, err := b.clientCtx.Client.Status(b.ctx) + if err != nil { + return false, err + } + + if !status.SyncInfo.CatchingUp { + return false, nil + } + + return map[string]any{ + "startingBlock": hexutil.Uint64(status.SyncInfo.EarliestBlockHeight), + "currentBlock": hexutil.Uint64(status.SyncInfo.LatestBlockHeight), + // "highestBlock": nil, // NA + // "pulledStates": nil, // NA + // "knownStates": nil, // NA + }, nil +} + +// RPCGasCap is the global gas cap for eth-call variants. +func (b *Backend) RPCGasCap() uint64 { + return b.cfg.JSONRPC.GasCap +} + +// RPCEVMTimeout is the global evm timeout for eth-call variants. +func (b *Backend) RPCEVMTimeout() time.Duration { + return b.cfg.JSONRPC.EVMTimeout +} + +// RPCFilterCap is the limit for total number of filters that can be created +func (b *Backend) RPCFilterCap() int32 { + return b.cfg.JSONRPC.FilterCap +} + +// RPCLogsCap defines the max number of results can be returned from single `eth_getLogs` query. +func (b *Backend) RPCLogsCap() int32 { + return b.cfg.JSONRPC.LogsCap +} + +// RPCBlockRangeCap defines the max block range allowed for `eth_getLogs` query. +func (b *Backend) RPCBlockRangeCap() int32 { + return b.cfg.JSONRPC.BlockRangeCap +} + +// RPCMinGasPrice returns the minimum gas price for a transaction obtained from +// the node config. If set value is 0, it will default to 20. +func (b *Backend) RPCMinGasPrice() int64 { + minGasPrice := b.cfg.GetMinGasPrices() + amt := minGasPrice.AmountOf(evm.EVMBankDenom).TruncateInt64() + if amt == 0 { + return eth.DefaultGasPrice + } + + return amt +} diff --git a/eth/rpc/backend/node_info_test.go b/eth/rpc/backend/node_info_test.go new file mode 100644 index 000000000..a41d2f66d --- /dev/null +++ b/eth/rpc/backend/node_info_test.go @@ -0,0 +1,45 @@ +package backend_test + +import ( + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/app/server/config" + "github.com/NibiruChain/nibiru/v2/eth" +) + +func (s *BackendSuite) TestAccounts() { + accounts, err := s.backend.Accounts() + s.Require().NoError(err) + s.Require().Greater(len(accounts), 0) + s.Require().Contains(accounts, gethcommon.BytesToAddress(s.node.ValAddress.Bytes())) +} + +func (s *BackendSuite) TestSyncing() { + syncing, err := s.backend.Syncing() + s.Require().NoError(err) + s.Require().False(syncing.(bool)) +} + +func (s *BackendSuite) TestRPCGasCap() { + s.Require().Equal(config.DefaultConfig().JSONRPC.GasCap, s.backend.RPCGasCap()) +} + +func (s *BackendSuite) TestRPCEVMTimeout() { + s.Require().Equal(config.DefaultConfig().JSONRPC.EVMTimeout, s.backend.RPCEVMTimeout()) +} + +func (s *BackendSuite) TestRPCFilterCap() { + s.Require().Equal(config.DefaultConfig().JSONRPC.FilterCap, s.backend.RPCFilterCap()) +} + +func (s *BackendSuite) TestRPCLogsCap() { + s.Require().Equal(config.DefaultConfig().JSONRPC.LogsCap, s.backend.RPCLogsCap()) +} + +func (s *BackendSuite) TestRPCBlockRangeCap() { + s.Require().Equal(config.DefaultConfig().JSONRPC.BlockRangeCap, s.backend.RPCBlockRangeCap()) +} + +func (s *BackendSuite) TestRPCMinGasPrice() { + s.Require().Equal(int64(eth.DefaultGasPrice), s.backend.RPCMinGasPrice()) +} diff --git a/eth/rpc/backend/nonce_test.go b/eth/rpc/backend/nonce_test.go new file mode 100644 index 000000000..983470ec4 --- /dev/null +++ b/eth/rpc/backend/nonce_test.go @@ -0,0 +1,79 @@ +package backend_test + +import ( + sdkmath "cosmossdk.io/math" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// TestNonceIncrementWithMultipleMsgsTx tests that the nonce is incremented correctly +// when multiple messages are included in a single transaction. +func (s *BackendSuite) TestNonceIncrementWithMultipleMsgsTx() { + // Test is broadcasting txs. Lock to avoid nonce conflicts. + testMutex.Lock() + defer testMutex.Unlock() + + nonce := s.getCurrentNonce(s.fundedAccEthAddr) + + // Create series of 3 tx messages. Expecting nonce to be incremented by 3 + creationTx := s.buildContractCreationTx(nonce, 1_500_000) + firstTransferTx := s.buildContractCallTx(testContractAddress, nonce+1, 100_000) + secondTransferTx := s.buildContractCallTx(testContractAddress, nonce+2, 100_000) + + // Create and broadcast SDK transaction + sdkTx := s.buildSDKTxWithEVMMessages( + creationTx, + firstTransferTx, + secondTransferTx, + ) + + // Broadcast transaction + rsp := s.broadcastSDKTx(sdkTx) + s.Assert().NotEqual(rsp.Code, 0) + s.Require().NoError(s.network.WaitForNextBlock()) + + // Expected nonce should be incremented by 3 + currentNonce := s.getCurrentNonce(s.fundedAccEthAddr) + s.Assert().Equal(nonce+3, currentNonce) + + // Assert all transactions included in block + for _, tx := range []gethcore.Transaction{creationTx, firstTransferTx, secondTransferTx} { + blockNum, blockHash, _ := WaitForReceipt(s, tx.Hash()) + s.Require().NotNil(blockNum) + s.Require().NotNil(blockHash) + } +} + +// buildSDKTxWithEVMMessages creates an SDK transaction with EVM messages +func (s *BackendSuite) buildSDKTxWithEVMMessages(txs ...gethcore.Transaction) sdk.Tx { + msgs := make([]sdk.Msg, len(txs)) + for i, tx := range txs { + msg := &evm.MsgEthereumTx{} + err := msg.FromEthereumTx(&tx) + s.Require().NoError(err) + msgs[i] = msg + } + + option, err := codectypes.NewAnyWithValue(&evm.ExtensionOptionsEthereumTx{}) + s.Require().NoError(err) + + txBuilder, _ := s.backend.ClientCtx().TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder) + txBuilder.SetExtensionOptions(option) + err = txBuilder.SetMsgs(msgs...) + s.Require().NoError(err) + + // Set fees for all messages + totalGas := uint64(0) + for _, tx := range txs { + totalGas += tx.Gas() + } + fees := sdk.NewCoins(sdk.NewCoin("unibi", sdkmath.NewIntFromUint64(totalGas))) + txBuilder.SetFeeAmount(fees) + txBuilder.SetGasLimit(totalGas) + + return txBuilder.GetTx() +} diff --git a/eth/rpc/backend/tracing.go b/eth/rpc/backend/tracing.go new file mode 100644 index 000000000..94bb8fbba --- /dev/null +++ b/eth/rpc/backend/tracing.go @@ -0,0 +1,262 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "encoding/json" + "fmt" + "math" + + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// TraceTransaction returns the structured logs created during the execution of EVM +// and returns them as a JSON object. +func (b *Backend) TraceTransaction(hash gethcommon.Hash, config *evm.TraceConfig) (any, error) { + // Get transaction by hash + transaction, err := b.GetTxByEthHash(hash) + if err != nil { + b.logger.Debug("tx not found", "hash", hash) + return nil, err + } + + // check if block number is 0 + if transaction.Height == 0 { + return nil, errors.New("genesis is not traceable") + } + + blk, err := b.TendermintBlockByNumber(rpc.BlockNumber(transaction.Height)) + if err != nil { + b.logger.Debug("block not found", "height", transaction.Height) + return nil, err + } + + // check tx index is not out of bound + if len(blk.Block.Txs) > math.MaxUint32 { + return nil, fmt.Errorf("tx count %d is overflowing", len(blk.Block.Txs)) + } + txsLen := uint32(len(blk.Block.Txs)) // #nosec G701 -- checked for int overflow already + if txsLen < transaction.TxIndex { + b.logger.Debug("tx index out of bounds", "index", transaction.TxIndex, "hash", hash.String(), "height", blk.Block.Height) + return nil, fmt.Errorf("transaction not included in block %v", blk.Block.Height) + } + + var predecessors []*evm.MsgEthereumTx + for _, txBz := range blk.Block.Txs[:transaction.TxIndex] { + tx, err := b.clientCtx.TxConfig.TxDecoder()(txBz) + if err != nil { + b.logger.Debug("failed to decode transaction in block", "height", blk.Block.Height, "error", err.Error()) + continue + } + for _, msg := range tx.GetMsgs() { + ethMsg, ok := msg.(*evm.MsgEthereumTx) + if !ok { + continue + } + + predecessors = append(predecessors, ethMsg) + } + } + + tx, err := b.clientCtx.TxConfig.TxDecoder()(blk.Block.Txs[transaction.TxIndex]) + if err != nil { + b.logger.Debug("tx not found", "hash", hash) + return nil, err + } + + // add predecessor messages in current cosmos tx + index := int(transaction.MsgIndex) // #nosec G701 + for i := 0; i < index; i++ { + ethMsg, ok := tx.GetMsgs()[i].(*evm.MsgEthereumTx) + if !ok { + continue + } + predecessors = append(predecessors, ethMsg) + } + + ethMessage, ok := tx.GetMsgs()[transaction.MsgIndex].(*evm.MsgEthereumTx) + if !ok { + b.logger.Debug("invalid transaction type", "type", fmt.Sprintf("%T", tx)) + return nil, fmt.Errorf("invalid transaction type %T", tx) + } + + nc, ok := b.clientCtx.Client.(tmrpcclient.NetworkClient) + if !ok { + return nil, errors.New("invalid rpc client") + } + + cp, err := nc.ConsensusParams(b.ctx, &blk.Block.Height) + if err != nil { + return nil, err + } + + traceTxRequest := evm.QueryTraceTxRequest{ + Msg: ethMessage, + Predecessors: predecessors, + BlockNumber: blk.Block.Height, + BlockTime: blk.Block.Time, + BlockHash: gethcommon.Bytes2Hex(blk.BlockID.Hash), + ProposerAddress: sdk.ConsAddress(blk.Block.ProposerAddress), + ChainId: b.chainID.Int64(), + BlockMaxGas: cp.ConsensusParams.Block.MaxGas, + } + + if config != nil { + traceTxRequest.TraceConfig = config + } + + // minus one to get the context of block beginning + contextHeight := transaction.Height - 1 + if contextHeight < 1 { + // 0 is a special value in `ContextWithHeight` + contextHeight = 1 + } + traceResult, err := b.queryClient.TraceTx(rpc.NewContextWithHeight(contextHeight), &traceTxRequest) + if err != nil { + return nil, err + } + + // Response format is unknown due to custom tracer config param + // More information can be found here https://geth.ethereum.org/docs/dapp/tracing-filtered + var decodedResult any + err = json.Unmarshal(traceResult.Data, &decodedResult) + if err != nil { + return nil, err + } + + return decodedResult, nil +} + +// TraceBlock configures a new tracer according to the provided configuration, and +// executes all the transactions contained within. The return value will be one item +// per transaction, dependent on the requested tracer. +func (b *Backend) TraceBlock(height rpc.BlockNumber, + config *evm.TraceConfig, + block *tmrpctypes.ResultBlock, +) ([]*evm.TxTraceResult, error) { + txs := block.Block.Txs + txsLength := len(txs) + + if txsLength == 0 { + // If there are no transactions return empty array + return []*evm.TxTraceResult{}, nil + } + + txDecoder := b.clientCtx.TxConfig.TxDecoder() + + var txsMessages []*evm.MsgEthereumTx + for i, tx := range txs { + decodedTx, err := txDecoder(tx) + if err != nil { + b.logger.Error("failed to decode transaction", "hash", txs[i].Hash(), "error", err.Error()) + continue + } + + for _, msg := range decodedTx.GetMsgs() { + ethMessage, ok := msg.(*evm.MsgEthereumTx) + if !ok { + // Just considers Ethereum transactions + continue + } + txsMessages = append(txsMessages, ethMessage) + } + } + + // minus one to get the context at the beginning of the block + contextHeight := height - 1 + if contextHeight < 1 { + // 0 is a special value for `ContextWithHeight`. + contextHeight = 1 + } + ctxWithHeight := rpc.NewContextWithHeight(int64(contextHeight)) + + nc, ok := b.clientCtx.Client.(tmrpcclient.NetworkClient) + if !ok { + return nil, errors.New("invalid rpc client") + } + + cp, err := nc.ConsensusParams(b.ctx, &block.Block.Height) + if err != nil { + return nil, err + } + + traceBlockRequest := &evm.QueryTraceBlockRequest{ + Txs: txsMessages, + TraceConfig: config, + BlockNumber: block.Block.Height, + BlockTime: block.Block.Time, + BlockHash: gethcommon.Bytes2Hex(block.BlockID.Hash), + ProposerAddress: sdk.ConsAddress(block.Block.ProposerAddress), + ChainId: b.chainID.Int64(), + BlockMaxGas: cp.ConsensusParams.Block.MaxGas, + } + + res, err := b.queryClient.TraceBlock(ctxWithHeight, traceBlockRequest) + if err != nil { + return nil, err + } + + decodedResults := make([]*evm.TxTraceResult, txsLength) + if err := json.Unmarshal(res.Data, &decodedResults); err != nil { + return nil, err + } + + return decodedResults, nil +} + +// TraceCall implements eth debug_traceCall method which lets you run an eth_call +// within the context of the given block execution using the final state of parent block as the base. +// Method returns the structured logs created during the execution of EVM. +// The method returns the same output as debug_traceTransaction. +// https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugtracecall +func (b *Backend) TraceCall( + txArgs evm.JsonTxArgs, + contextBlock rpc.BlockNumber, + config *evm.TraceConfig, +) (any, error) { + blk, err := b.TendermintBlockByNumber(contextBlock) + if err != nil { + b.logger.Debug("block not found", "contextBlock", contextBlock) + return nil, err + } + nc, ok := b.clientCtx.Client.(tmrpcclient.NetworkClient) + if !ok { + return nil, errors.New("invalid rpc client") + } + + cp, err := nc.ConsensusParams(b.ctx, &blk.Block.Height) + if err != nil { + return nil, err + } + + traceTxRequest := evm.QueryTraceTxRequest{ + Msg: txArgs.ToMsgEthTx(), + Predecessors: nil, + BlockNumber: blk.Block.Height, + BlockTime: blk.Block.Time, + BlockHash: gethcommon.Bytes2Hex(blk.BlockID.Hash), + ProposerAddress: sdk.ConsAddress(blk.Block.ProposerAddress), + ChainId: b.chainID.Int64(), + BlockMaxGas: cp.ConsensusParams.Block.MaxGas, + } + + if config != nil { + traceTxRequest.TraceConfig = config + } + traceResult, err := b.queryClient.TraceCall(rpc.NewContextWithHeight(contextBlock.Int64()), &traceTxRequest) + if err != nil { + return nil, err + } + var decodedResult any + err = json.Unmarshal(traceResult.Data, &decodedResult) + if err != nil { + return nil, err + } + return decodedResult, nil +} diff --git a/eth/rpc/backend/tracing_test.go b/eth/rpc/backend/tracing_test.go new file mode 100644 index 000000000..e8c9a090c --- /dev/null +++ b/eth/rpc/backend/tracing_test.go @@ -0,0 +1,134 @@ +package backend_test + +import ( + "math/big" + "strings" + + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +var traceConfig = &evm.TraceConfig{ + Tracer: "callTracer", + TracerConfig: &evm.TracerConfig{ + OnlyTopCall: true, + }, +} + +func (s *BackendSuite) TestTraceTransaction() { + testCases := []struct { + name string + txHash gethcommon.Hash + wantErr string + }{ + { + name: "sad: tx not found", + txHash: gethcommon.BytesToHash([]byte("0x0")), + wantErr: "not found", + }, + { + name: "happy: tx found", + txHash: transferTxHash, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res, err := s.backend.TraceTransaction( + tc.txHash, + traceConfig, + ) + if tc.wantErr != "" { + s.ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + s.Require().NotNil(res) + AssertTraceCall(s, res.(map[string]interface{})) + }) + } +} + +func (s *BackendSuite) TestTraceBlock() { + tmBlockWithTx, err := s.backend.TendermintBlockByNumber(transferTxBlockNumber) + s.Require().NoError(err) + + blockNumberWithoutTx := rpc.NewBlockNumber(big.NewInt(1)) + tmBlockWithoutTx, err := s.backend.TendermintBlockByNumber(1) + s.Require().NoError(err) + + testCases := []struct { + name string + blockNumber rpc.BlockNumber + tmBlock *tmrpctypes.ResultBlock + txCount int + }{ + { + name: "happy: block without txs", + blockNumber: blockNumberWithoutTx, + tmBlock: tmBlockWithoutTx, + txCount: 0, + }, + { + name: "happy: block with txs", + blockNumber: transferTxBlockNumber, + tmBlock: tmBlockWithTx, + txCount: 1, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + res, err := s.backend.TraceBlock( + tc.blockNumber, + traceConfig, + tc.tmBlock, + ) + s.Require().NoError(err) + s.Require().Equal(tc.txCount, len(res)) + if tc.txCount > 0 { + AssertTraceCall(s, res[0].Result.(map[string]interface{})) + } + }) + } +} + +func (s *BackendSuite) TestTraceCall() { + block, err := s.backend.BlockNumber() + s.Require().NoError(err) + nonce, err := s.backend.GetTransactionCount(s.fundedAccEthAddr, rpc.BlockNumber(block)) + s.NoError(err) + gas := hexutil.Uint64(evm.NativeToWei(big.NewInt(int64(params.TxGas))).Uint64()) + amountToSendHex := hexutil.Big(*amountToSend) + + txArgs := evm.JsonTxArgs{ + Nonce: nonce, + From: &s.fundedAccEthAddr, + To: &recipient, + Value: &amountToSendHex, + Gas: &gas, + } + s.Require().NoError(err) + + res, err := s.backend.TraceCall( + txArgs, + rpc.BlockNumber(block), + traceConfig, + ) + s.Require().NoError(err) + s.Require().NotNil(res) + AssertTraceCall(s, res.(map[string]interface{})) +} + +func AssertTraceCall(s *BackendSuite, trace map[string]interface{}) { + s.Require().Equal("CALL", trace["type"]) + s.Require().Equal(strings.ToLower(s.fundedAccEthAddr.Hex()), trace["from"]) + s.Require().Equal(strings.ToLower(recipient.Hex()), trace["to"]) + s.Require().Equal("0x"+gethcommon.Bytes2Hex(amountToSend.Bytes()), trace["value"]) +} diff --git a/eth/rpc/backend/tx_info.go b/eth/rpc/backend/tx_info.go new file mode 100644 index 000000000..ed0d45fee --- /dev/null +++ b/eth/rpc/backend/tx_info.go @@ -0,0 +1,454 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "encoding/json" + "fmt" + "math" + "math/big" + + errorsmod "cosmossdk.io/errors" + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// GetTransactionByHash returns the Ethereum format transaction identified by +// Ethereum transaction hash. If the transaction is not found or has been +// discarded from a pruning node, this resolves to nil. +func (b *Backend) GetTransactionByHash(txHash gethcommon.Hash) (*rpc.EthTxJsonRPC, error) { + res, err := b.GetTxByEthHash(txHash) + if err != nil { + return b.getTransactionByHashPending(txHash) + } + + block, err := b.TendermintBlockByNumber(rpc.BlockNumber(res.Height)) + if err != nil { + return nil, err + } + + tx, err := b.clientCtx.TxConfig.TxDecoder()(block.Block.Txs[res.TxIndex]) + if err != nil { + return nil, err + } + + // the `res.MsgIndex` is inferred from tx index, should be within the bound. + msg, ok := tx.GetMsgs()[res.MsgIndex].(*evm.MsgEthereumTx) + if !ok { + return nil, errors.New("invalid ethereum tx") + } + + blockRes, err := b.TendermintBlockResultByNumber(&block.Block.Height) + if err != nil { + b.logger.Debug("block result not found", "height", block.Block.Height, "error", err.Error()) + return nil, nil + } + + if res.EthTxIndex == -1 { + // Fallback to find tx index by iterating all valid eth transactions + msgs := b.EthMsgsFromTendermintBlock(block, blockRes) + for i := range msgs { + if msgs[i].Hash == eth.EthTxHashToString(txHash) { + if i > math.MaxInt32 { + return nil, errors.New("tx index overflow") + } + res.EthTxIndex = int32(i) //#nosec G701 -- checked for int overflow already + break + } + } + } + // if we still unable to find the eth tx index, return error, shouldn't happen. + if res.EthTxIndex == -1 { + return nil, errors.New("can't find index of ethereum tx") + } + + baseFeeWei, err := b.BaseFeeWei(blockRes) + if err != nil { + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", blockRes.Height, "error", err) + } + + height := uint64(res.Height) //#nosec G701 -- checked for int overflow already + index := uint64(res.EthTxIndex) //#nosec G701 -- checked for int overflow already + return rpc.NewRPCTxFromMsg( + msg, + gethcommon.BytesToHash(block.BlockID.Hash.Bytes()), + height, + index, + baseFeeWei, + b.chainID, + ) +} + +// getTransactionByHashPending find pending tx from mempool +func (b *Backend) getTransactionByHashPending(txHash gethcommon.Hash) (*rpc.EthTxJsonRPC, error) { + hexTx := txHash.Hex() + // try to find tx in mempool + txs, err := b.PendingTransactions() + if err != nil { + b.logger.Debug("tx not found", "hash", hexTx, "error", err.Error()) + return nil, nil + } + + for _, tx := range txs { + msg, err := evm.UnwrapEthereumMsg(tx, txHash) + if err != nil { + // not ethereum tx + continue + } + + if msg.Hash == hexTx { + // use zero block values since it's not included in a block yet + rpctx, err := rpc.NewRPCTxFromMsg( + msg, + gethcommon.Hash{}, + uint64(0), + uint64(0), + nil, + b.chainID, + ) + if err != nil { + return nil, err + } + return rpctx, nil + } + } + + b.logger.Debug("tx not found", "hash", hexTx) + return nil, nil +} + +// TransactionReceipt represents the results of a transaction. TransactionReceipt +// is an extension of gethcore.Receipt, the response type for the +// "eth_getTransactionReceipt" JSON-RPC method. +// Reason being, the gethcore.Receipt struct has an incorrect JSON struct tag on one +// field and doesn't marshal JSON as expected, so we embed and extend it here. +type TransactionReceipt struct { + gethcore.Receipt + + ContractAddress *gethcommon.Address + From gethcommon.Address + To *gethcommon.Address + EffectiveGasPrice *hexutil.Big +} + +// MarshalJSON is necessary because without it non gethcore.Receipt fields are omitted +func (r *TransactionReceipt) MarshalJSON() ([]byte, error) { + // Marshal / unmarshal gethcore.Receipt to produce map[string]interface{} + receiptJson, err := json.Marshal(r.Receipt) + if err != nil { + return nil, err + } + + var output map[string]interface{} + if err := json.Unmarshal(receiptJson, &output); err != nil { + return nil, err + } + + // Add extra (non gethcore.Receipt) fields: + if r.ContractAddress != nil && *r.ContractAddress != (gethcommon.Address{}) { + output["contractAddress"] = r.ContractAddress + } + if r.From != (gethcommon.Address{}) { + output["from"] = r.From + } + if r.To != nil { + output["to"] = r.To + } + if r.EffectiveGasPrice != nil { + output["effectiveGasPrice"] = r.EffectiveGasPrice + } + // delete deprecated (pre Byzantium) key which is always set to 0x and fails parsing within hardhat + delete(output, "root") + + return json.Marshal(output) +} + +// GetTransactionReceipt returns the transaction receipt identified by hash. +func (b *Backend) GetTransactionReceipt(hash gethcommon.Hash) (*TransactionReceipt, error) { + hexTx := hash.Hex() + b.logger.Debug("eth_getTransactionReceipt", "hash", hexTx) + + res, err := b.GetTxByEthHash(hash) + if err != nil { + b.logger.Debug("tx not found", "hash", hexTx, "error", err.Error()) + return nil, nil + } + resBlock, err := b.TendermintBlockByNumber(rpc.BlockNumber(res.Height)) + if err != nil { + b.logger.Debug("block not found", "height", res.Height, "error", err.Error()) + return nil, nil + } + tx, err := b.clientCtx.TxConfig.TxDecoder()(resBlock.Block.Txs[res.TxIndex]) + if err != nil { + b.logger.Debug("decoding failed", "error", err.Error()) + return nil, fmt.Errorf("failed to decode tx: %w", err) + } + ethMsg := tx.GetMsgs()[res.MsgIndex].(*evm.MsgEthereumTx) + + txData, err := evm.UnpackTxData(ethMsg.Data) + if err != nil { + b.logger.Error("failed to unpack tx data", "error", err.Error()) + return nil, err + } + + cumulativeGasUsed := uint64(0) + blockRes, err := b.TendermintBlockResultByNumber(&res.Height) + if err != nil { + b.logger.Debug("failed to retrieve block results", "height", res.Height, "error", err.Error()) + return nil, nil + } + for _, txResult := range blockRes.TxsResults[0:res.TxIndex] { + cumulativeGasUsed += uint64(txResult.GasUsed) // #nosec G701 -- checked for int overflow already + } + cumulativeGasUsed += res.CumulativeGasUsed + + var status uint64 = gethcore.ReceiptStatusSuccessful + if res.Failed { + status = gethcore.ReceiptStatusFailed + } + + chainID := b.ChainID() + + from, err := ethMsg.GetSender(chainID.ToInt()) + if err != nil { + return nil, err + } + + // parse tx logs from events + msgIndex := int(res.MsgIndex) // #nosec G701 -- checked for int overflow already + logs, err := TxLogsFromEvents(blockRes.TxsResults[res.TxIndex].Events, msgIndex) + if err != nil { + b.logger.Debug("failed to parse logs", "hash", hexTx, "error", err.Error()) + } + + if res.EthTxIndex == -1 { + // Fallback to find tx index by iterating all valid eth transactions + msgs := b.EthMsgsFromTendermintBlock(resBlock, blockRes) + for i := range msgs { + if msgs[i].Hash == hexTx { + res.EthTxIndex = int32(i) // #nosec G701 + break + } + } + } + // return error if still unable to find the eth tx index + if res.EthTxIndex == -1 { + return nil, errors.New("can't find index of ethereum tx") + } + + receipt := TransactionReceipt{ + Receipt: gethcore.Receipt{ + Type: ethMsg.AsTransaction().Type(), + + // Consensus fields: These fields are defined by the Etheruem Yellow Paper + Status: status, + CumulativeGasUsed: cumulativeGasUsed, + Bloom: gethcore.BytesToBloom(gethcore.LogsBloom(logs)), + Logs: logs, + + // Implementation fields: These fields are added by geth when processing a transaction. + // They are stored in the chain database. + TxHash: hash, + GasUsed: res.GasUsed, + + BlockHash: gethcommon.BytesToHash(resBlock.Block.Header.Hash()), + BlockNumber: big.NewInt(res.Height), + TransactionIndex: uint(res.EthTxIndex), + }, + ContractAddress: nil, + From: from, + To: txData.GetTo(), + } + + if logs == nil { + receipt.Logs = []*gethcore.Log{} + } + + // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation + if txData.GetTo() == nil { + addr := crypto.CreateAddress(from, txData.GetNonce()) + receipt.ContractAddress = &addr + } + + if dynamicTx, ok := txData.(*evm.DynamicFeeTx); ok { + baseFeeWei, err := b.BaseFeeWei(blockRes) + if err != nil { + // tolerate the error for pruned node. + b.logger.Error("fetch basefee failed, node is pruned?", "height", res.Height, "error", err) + } else { + receipt.EffectiveGasPrice = (*hexutil.Big)(dynamicTx.EffectiveGasPriceWeiPerGas(baseFeeWei)) + } + } + return &receipt, nil +} + +// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index. +func (b *Backend) GetTransactionByBlockHashAndIndex(hash gethcommon.Hash, idx hexutil.Uint) (*rpc.EthTxJsonRPC, error) { + b.logger.Debug("eth_getTransactionByBlockHashAndIndex", "hash", hash.Hex(), "index", idx) + sc, ok := b.clientCtx.Client.(tmrpcclient.SignClient) + if !ok { + return nil, errors.New("invalid rpc client") + } + + block, err := sc.BlockByHash(b.ctx, hash.Bytes()) + if err != nil { + b.logger.Debug("block not found", "hash", hash.Hex(), "error", err.Error()) + return nil, nil + } + + if block.Block == nil { + b.logger.Debug("block not found", "hash", hash.Hex()) + return nil, nil + } + + return b.GetTransactionByBlockAndIndex(block, idx) +} + +// GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index. +func (b *Backend) GetTransactionByBlockNumberAndIndex(blockNum rpc.BlockNumber, idx hexutil.Uint) (*rpc.EthTxJsonRPC, error) { + b.logger.Debug("eth_getTransactionByBlockNumberAndIndex", "number", blockNum, "index", idx) + + block, err := b.TendermintBlockByNumber(blockNum) + if err != nil { + b.logger.Debug("block not found", "height", blockNum.Int64(), "error", err.Error()) + return nil, nil + } + + if block.Block == nil { + b.logger.Debug("block not found", "height", blockNum.Int64()) + return nil, nil + } + + return b.GetTransactionByBlockAndIndex(block, idx) +} + +// GetTxByEthHash uses `/tx_query` to find transaction by ethereum tx hash +func (b *Backend) GetTxByEthHash(hash gethcommon.Hash) (*eth.TxResult, error) { + if b.evmTxIndexer != nil { + return b.evmTxIndexer.GetByTxHash(hash) + } + + // fallback to tendermint tx evmTxIndexer + query := fmt.Sprintf("%s.%s='%s'", evm.PendingEthereumTxEvent, evm.PendingEthereumTxEventAttrEthHash, hash.Hex()) + + txResult, err := b.queryTendermintTxIndexer(query, func(txs *rpc.ParsedTxs) *rpc.ParsedTx { + return txs.GetTxByHash(hash) + }) + if err != nil { + return nil, errorsmod.Wrapf(err, "GetTxByEthHash(%s)", hash.Hex()) + } + return txResult, nil +} + +// GetTxByTxIndex uses `/tx_query` to find transaction by tx index of valid ethereum txs +func (b *Backend) GetTxByTxIndex(height int64, index uint) (*eth.TxResult, error) { + int32Index := int32(index) // #nosec G701 -- checked for int overflow already + if b.evmTxIndexer != nil { + return b.evmTxIndexer.GetByBlockAndIndex(height, int32Index) + } + + // fallback to tendermint tx evmTxIndexer + query := fmt.Sprintf("tx.height=%d AND %s.%s=%d", + height, + evm.PendingEthereumTxEvent, + evm.PendingEthereumTxEventAttrIndex, + index, + ) + txResult, err := b.queryTendermintTxIndexer(query, func(txs *rpc.ParsedTxs) *rpc.ParsedTx { + return txs.GetTxByTxIndex(int(index)) // #nosec G701 -- checked for int overflow already + }) + if err != nil { + return nil, errorsmod.Wrapf(err, "GetTxByTxIndex %d %d", height, index) + } + return txResult, nil +} + +// queryTendermintTxIndexer query tx in tendermint tx evmTxIndexer +func (b *Backend) queryTendermintTxIndexer(query string, txGetter func(*rpc.ParsedTxs) *rpc.ParsedTx) (*eth.TxResult, error) { + resTxs, err := b.clientCtx.Client.TxSearch(b.ctx, query, false, nil, nil, "") + if err != nil { + return nil, err + } + if len(resTxs.Txs) == 0 { + return nil, errors.New("ethereum tx not found") + } + txResult := resTxs.Txs[0] + isValidEnough, reason := rpc.TxIsValidEnough(&txResult.TxResult) + if !isValidEnough { + return nil, errors.Errorf("invalid ethereum tx: %s", reason) + } + + var tx sdk.Tx + if txResult.TxResult.Code != 0 { + // it's only needed when the tx exceeds block gas limit + tx, err = b.clientCtx.TxConfig.TxDecoder()(txResult.Tx) + if err != nil { + return nil, fmt.Errorf("invalid ethereum tx") + } + } + + return rpc.ParseTxIndexerResult(txResult, tx, txGetter) +} + +// GetTransactionByBlockAndIndex is the common code shared by `GetTransactionByBlockNumberAndIndex` and `GetTransactionByBlockHashAndIndex`. +func (b *Backend) GetTransactionByBlockAndIndex(block *tmrpctypes.ResultBlock, idx hexutil.Uint) (*rpc.EthTxJsonRPC, error) { + blockRes, err := b.TendermintBlockResultByNumber(&block.Block.Height) + if err != nil { + return nil, nil + } + + var msg *evm.MsgEthereumTx + // find in tx evmTxIndexer + res, err := b.GetTxByTxIndex(block.Block.Height, uint(idx)) + if err == nil { + tx, err := b.clientCtx.TxConfig.TxDecoder()(block.Block.Txs[res.TxIndex]) + if err != nil { + b.logger.Debug("invalid ethereum tx", "height", block.Block.Header, "index", idx) + return nil, nil + } + + var ok bool + // msgIndex is inferred from tx events, should be within bound. + msg, ok = tx.GetMsgs()[res.MsgIndex].(*evm.MsgEthereumTx) + if !ok { + b.logger.Debug("invalid ethereum tx", "height", block.Block.Header, "index", idx) + return nil, nil + } + } else { + i := int(idx) // #nosec G701 + ethMsgs := b.EthMsgsFromTendermintBlock(block, blockRes) + if i >= len(ethMsgs) { + b.logger.Debug("block txs index out of bound", "index", i) + return nil, nil + } + + msg = ethMsgs[i] + } + + baseFeeWei, err := b.BaseFeeWei(blockRes) + if err != nil { + // handle the error for pruned node. + b.logger.Error("failed to fetch Base Fee from prunned block. Check node prunning configuration", "height", block.Block.Height, "error", err) + } + + height := uint64(block.Block.Height) // #nosec G701 -- checked for int overflow already + index := uint64(idx) // #nosec G701 -- checked for int overflow already + return rpc.NewRPCTxFromMsg( + msg, + gethcommon.BytesToHash(block.Block.Hash()), + height, + index, + baseFeeWei, + b.chainID, + ) +} diff --git a/eth/rpc/backend/tx_info_test.go b/eth/rpc/backend/tx_info_test.go new file mode 100644 index 000000000..d0d1eb0e4 --- /dev/null +++ b/eth/rpc/backend/tx_info_test.go @@ -0,0 +1,218 @@ +package backend_test + +import ( + "encoding/json" + "math/big" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *BackendSuite) TestGetTransactionByHash() { + testCases := []struct { + name string + txHash gethcommon.Hash + wantTxFound bool + }{ + { + name: "happy: tx found", + txHash: transferTxHash, + wantTxFound: true, + }, + { + name: "sad: tx not found", + txHash: gethcommon.BytesToHash([]byte("0x0")), + wantTxFound: false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + txResponse, err := s.backend.GetTransactionByHash(tc.txHash) + if !tc.wantTxFound { + s.Require().Nil(txResponse) + return + } + s.Require().NoError(err) + s.Require().NotNil(txResponse) + s.Require().Equal(tc.txHash, txResponse.Hash) + s.Require().Equal(s.fundedAccEthAddr, txResponse.From) + s.Require().Equal(&recipient, txResponse.To) + s.Require().Equal(amountToSend, txResponse.Value.ToInt()) + }) + } +} + +func (s *BackendSuite) TestGetTransactionReceipt() { + testCases := []struct { + name string + txHash gethcommon.Hash + wantTxFound bool + }{ + { + name: "happy: tx found", + txHash: transferTxHash, + wantTxFound: true, + }, + { + name: "sad: tx not found", + txHash: gethcommon.BytesToHash([]byte("0x0")), + wantTxFound: false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + receipt, err := s.backend.GetTransactionReceipt(tc.txHash) + if !tc.wantTxFound { + s.Require().Nil(receipt) + return + } + s.Require().NoError(err) + s.Require().NotNil(receipt) + + // Check fields + s.Equal(s.fundedAccEthAddr, receipt.From) + s.Equal(&recipient, receipt.To) + s.Greater(receipt.GasUsed, uint64(0)) + s.Equal(receipt.GasUsed, receipt.CumulativeGasUsed) + s.Equal(tc.txHash, receipt.TxHash) + s.Nil(receipt.ContractAddress) + s.Require().Equal(gethcore.ReceiptStatusSuccessful, receipt.Status) + }) + } +} + +func (s *BackendSuite) TestGetTransactionByBlockHashAndIndex() { + blockWithTx, err := s.backend.GetBlockByNumber(transferTxBlockNumber, false) + s.Require().NoError(err) + blockHash := gethcommon.BytesToHash(blockWithTx["hash"].(hexutil.Bytes)) + + testCases := []struct { + name string + blockHash gethcommon.Hash + txIndex uint + wantTxFound bool + }{ + { + name: "happy: tx found", + blockHash: blockHash, + txIndex: 0, + wantTxFound: true, + }, + { + name: "sad: block not found", + blockHash: gethcommon.BytesToHash([]byte("0x0")), + txIndex: 1, + wantTxFound: false, + }, + { + name: "sad: tx not found", + blockHash: blockHash, + txIndex: 9999, + wantTxFound: false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + tx, err := s.backend.GetTransactionByBlockHashAndIndex(tc.blockHash, hexutil.Uint(tc.txIndex)) + if !tc.wantTxFound { + s.Require().Nil(tx) + return + } + s.Require().NoError(err) + s.Require().NotNil(tx) + AssertTxResults(s, tx, transferTxHash) + }) + } +} + +func (s *BackendSuite) TestGetTransactionByBlockNumberAndIndex() { + testCases := []struct { + name string + blockNumber rpc.BlockNumber + txIndex uint + wantTxFound bool + }{ + { + name: "happy: tx found", + blockNumber: transferTxBlockNumber, + txIndex: 0, + wantTxFound: true, + }, + { + name: "sad: block not found", + blockNumber: rpc.NewBlockNumber(big.NewInt(9999999)), + txIndex: 0, + wantTxFound: false, + }, + { + name: "sad: tx not found", + blockNumber: transferTxBlockNumber, + txIndex: 9999, + wantTxFound: false, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + tx, err := s.backend.GetTransactionByBlockNumberAndIndex(tc.blockNumber, hexutil.Uint(tc.txIndex)) + if !tc.wantTxFound { + s.Require().Nil(tx) + return + } + s.Require().NoError(err) + s.Require().NotNil(tx) + AssertTxResults(s, tx, transferTxHash) + }) + } +} + +func AssertTxResults(s *BackendSuite, tx *rpc.EthTxJsonRPC, expectedTxHash gethcommon.Hash) { + s.Require().Equal(s.fundedAccEthAddr, tx.From) + s.Require().Equal(&recipient, tx.To) + s.Require().Greater(tx.Gas, uint64(0)) + s.Require().Equal(expectedTxHash, tx.Hash) + s.Require().Equal(uint64(0), uint64(*tx.TransactionIndex)) +} + +func (s *BackendSuite) TestReceiptMarshalJson() { + toAddr := evmtest.NewEthPrivAcc().EthAddr + tr := backend.TransactionReceipt{ + Receipt: gethcore.Receipt{ + Type: 0, + PostState: []byte{}, + Status: 0, + CumulativeGasUsed: 0, + Bloom: [256]byte{}, + Logs: []*gethcore.Log{}, + TxHash: [32]byte{}, + ContractAddress: [20]byte{}, + GasUsed: 0, + BlockHash: [32]byte{}, + BlockNumber: &big.Int{}, + TransactionIndex: 0, + }, + ContractAddress: nil, + From: evmtest.NewEthPrivAcc().EthAddr, + To: &toAddr, + EffectiveGasPrice: (*hexutil.Big)(big.NewInt(1)), + } + + jsonBz, err := tr.MarshalJSON() + s.Require().NoError(err) + + gethReceipt := new(gethcore.Receipt) + err = json.Unmarshal(jsonBz, gethReceipt) + s.Require().NoError(err) + + receipt := new(backend.TransactionReceipt) + err = json.Unmarshal(jsonBz, receipt) + s.Require().NoError(err) +} diff --git a/eth/rpc/backend/tx_logs_test.go b/eth/rpc/backend/tx_logs_test.go new file mode 100644 index 000000000..3c5a81e4f --- /dev/null +++ b/eth/rpc/backend/tx_logs_test.go @@ -0,0 +1,300 @@ +package backend_test + +import ( + "fmt" + "math/big" + + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +// TestEthLogs checks that eth txs as well as funtoken txs produce tx_logs events and update tx index properly. +// To check that, we send a series of transactions: +// - 1: simple eth transfer +// - 2: deploying erc20 contract +// - 3. creating funtoken from erc20 +// - 4: creating funtoken from coin +// - 5. converting coin to erc20 +// - 6. converting erc20 born token to coin via precompile +// Each tx should emit some tx logs and emit proper tx index within ethereum tx event. +func (s *BackendSuite) TestLogs() { + // Test is broadcasting txs. Lock to avoid nonce conflicts. + testMutex.Lock() + defer testMutex.Unlock() + + // Start with fresh block + s.Require().NoError(s.network.WaitForNextBlock()) + + s.T().Log("TX1: Send simple nibi transfer") + randomEthAddr := evmtest.NewEthPrivAcc().EthAddr + txHashFirst := s.SendNibiViaEthTransfer(randomEthAddr, amountToSend, false) + + s.T().Log("TX2: Deploy ERC20 contract") + _, erc20ContractAddr := s.DeployTestContract(false) + erc20Addr, _ := eth.NewEIP55AddrFromStr(erc20ContractAddr.String()) + + s.T().Log("TX3: Create FunToken from ERC20") + nonce := s.getCurrentNonce(eth.NibiruAddrToEthAddr(s.node.Address)) + txResp, err := s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ + Sender: s.node.Address.String(), + FromErc20: &erc20Addr, + }) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().Equal( + uint32(0), + txResp.Code, + fmt.Sprintf("Failed to create FunToken from ERC20. RawLog: %s", txResp.RawLog), + ) + + s.T().Log("TX4: Create FunToken from unibi coin") + nonce++ + erc20FromCoinAddr := crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, s.getCurrentNonce(evm.EVM_MODULE_ADDRESS)+1) + + txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{ + Sender: s.node.Address.String(), + FromBankDenom: evm.EVMBankDenom, + }) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().Equal( + uint32(0), + txResp.Code, + fmt.Sprintf("Failed to create FunToken from unibi coin. RawLog: %s", txResp.RawLog), + ) + + s.T().Log("TX5: Convert coin to EVM") + nonce++ + txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgConvertCoinToEvm{ + Sender: s.node.Address.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(1)), + ToEthAddr: eth.EIP55Addr{ + Address: s.fundedAccEthAddr, + }, + }) + s.Require().NoError(err) + s.Require().NotNil(txResp) + s.Require().Equal( + uint32(0), + txResp.Code, + fmt.Sprintf("Failed converting coin to evm. RawLog: %s", txResp.RawLog), + ) + + s.T().Log("TX6: Send erc20 token to coin using precompile") + randomNibiAddress := testutil.AccAddress() + packedArgsPass, err := embeds.SmartContract_FunToken.ABI.Pack( + "sendToBank", + erc20Addr.Address, + big.NewInt(1), + randomNibiAddress.String(), + ) + s.Require().NoError(err) + txHashLast := SendTransaction( + s, + &gethcore.LegacyTx{ + Nonce: s.getCurrentNonce(s.fundedAccEthAddr), + To: &precompile.PrecompileAddr_FunToken, + Data: packedArgsPass, + Gas: 1_500_000, + GasPrice: big.NewInt(1), + }, + false, + ) + + // Wait for all txs to be included in a block + blockNumFirstTx, _, _ := WaitForReceipt(s, txHashFirst) + blockNumLastTx, _, _ := WaitForReceipt(s, txHashLast) + s.Require().NotNil(blockNumFirstTx) + s.Require().NotNil(blockNumLastTx) + + // Check tx logs for each tx + type logsCheck struct { + txInfo string + logs []*gethcore.Log + expectEthTx bool + } + checks := []logsCheck{ + { + txInfo: "TX1 - simple eth transfer, should have empty logs", + logs: nil, + expectEthTx: true, + }, + { + txInfo: "TX2 - deploying erc20 contract, should have logs", + logs: []*gethcore.Log{ + // minting initial balance to the account + { + Address: erc20ContractAddr, + Topics: []gethcommon.Hash{ + crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), + gethcommon.Address{}.Hash(), + s.fundedAccEthAddr.Hash(), + }, + }, + }, + expectEthTx: true, + }, + { + txInfo: "TX3 - create FunToken from ERC20, no eth tx, no logs", + logs: nil, + expectEthTx: false, + }, + { + txInfo: "TX4 - create FunToken from unibi coin, no eth tx, logs for contract deployment", + logs: []*gethcore.Log{ + // contract ownership to evm module + { + Address: erc20FromCoinAddr, + Topics: []gethcommon.Hash{ + crypto.Keccak256Hash([]byte("OwnershipTransferred(address,address)")), + gethcommon.Address{}.Hash(), + evm.EVM_MODULE_ADDRESS.Hash(), + }, + }, + }, + expectEthTx: false, + }, + { + txInfo: "TX5 - Convert coin to EVM, no eth tx, logs for minting tokens to the account", + logs: []*gethcore.Log{ + // minting to the account + { + Address: erc20FromCoinAddr, + Topics: []gethcommon.Hash{ + crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), + gethcommon.Address{}.Hash(), + s.fundedAccEthAddr.Hash(), + }, + }, + }, + expectEthTx: false, + }, + { + txInfo: "TX6 - Send erc20 token to coin using precompile, eth tx, logs for transferring tokens to evm module", + logs: []*gethcore.Log{ + // transfer from account to evm module + { + Address: erc20ContractAddr, + Topics: []gethcommon.Hash{ + crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), + s.fundedAccEthAddr.Hash(), + evm.EVM_MODULE_ADDRESS.Hash(), + }, + }, + }, + expectEthTx: true, + }, + } + + // Getting block results. Note: txs could be included in more than one block + blockNumber := blockNumFirstTx.Int64() + blockRes, err := s.backend.TendermintBlockResultByNumber(&blockNumber) + s.Require().NoError(err) + s.Require().NotNil(blockRes) + txIndex := 0 + ethTxIndex := 0 + for idx, check := range checks { + if txIndex+1 > len(blockRes.TxsResults) { + blockNumber++ + if blockNumber > blockNumLastTx.Int64() { + s.Fail("TX %d not found in block results", idx) + } + txIndex = 0 + ethTxIndex = 0 + blockRes, err = s.backend.TendermintBlockResultByNumber(&blockNumber) + s.Require().NoError(err) + s.Require().NotNil(blockRes) + } + s.assertTxLogsAndTxIndex(blockRes, txIndex, ethTxIndex, check.logs, check.expectEthTx, check.txInfo) + txIndex++ + if check.expectEthTx { + ethTxIndex++ + } + } +} + +// assertTxLogsAndTxIndex gets tx results from the block and checks tx logs and tx index. +func (s *BackendSuite) assertTxLogsAndTxIndex( + blockRes *tmrpctypes.ResultBlockResults, + txIndex int, + ethTxIndex int, + expectedTxLogs []*gethcore.Log, + expectedEthTx bool, + txInfo string, +) { + txResults := blockRes.TxsResults[txIndex] + s.Require().Equal(uint32(0x0), txResults.Code, "tx failed, %s. RawLog: %s", txInfo, txResults.Log) + + events := blockRes.TxsResults[txIndex].Events + + foundEthTx := false + for _, event := range events { + if event.Type == evm.TypeUrlEventTxLog { + eventTxLog, err := evm.EventTxLogFromABCIEvent(event) + s.Require().NoError(err) + + logs := evm.LogsToEthereum(eventTxLog.Logs) + if len(expectedTxLogs) > 0 { + s.Require().GreaterOrEqual(len(logs), len(expectedTxLogs)) + s.assertTxLogsMatch(expectedTxLogs, logs, txInfo) + } else { + s.Require().Nil(logs) + } + } + if event.Type == evm.TypeUrlEventEthereumTx { + foundEthTx = true + if !expectedEthTx { + s.Fail("unexpected EventEthereumTx event for non-eth tx, %s", txInfo) + } + ethereumTx, err := evm.EventEthereumTxFromABCIEvent(event) + s.Require().NoError(err) + s.Require().Equal( + fmt.Sprintf("%d", ethTxIndex), + ethereumTx.Index, + "tx index mismatch, %s", txInfo, + ) + } + } + if expectedEthTx && !foundEthTx { + s.Fail("expected EventEthereumTx event not found, %s", txInfo) + } +} + +// assertTxLogsMatch checks that actual tx logs include the expected logs +func (s *BackendSuite) assertTxLogsMatch( + expectedLogs []*gethcore.Log, + actualLogs []*gethcore.Log, + txInfo string, +) { + for idx, expectedLog := range expectedLogs { + actualLog := actualLogs[idx] + s.Require().Equal( + expectedLog.Address, + actualLog.Address, fmt.Sprintf("log contract address mismatch, log index %d, %s", idx, txInfo), + ) + + s.Require().Equal( + len(expectedLog.Topics), + len(actualLog.Topics), + fmt.Sprintf("topics length mismatch, log index %d, %s", idx, txInfo), + ) + + for idx, topic := range expectedLog.Topics { + s.Require().Equal( + topic, + actualLog.Topics[idx], + fmt.Sprintf("topic mismatch, log index %d, %s", idx, txInfo), + ) + } + } +} diff --git a/eth/rpc/backend/utils.go b/eth/rpc/backend/utils.go new file mode 100644 index 000000000..f4e49046d --- /dev/null +++ b/eth/rpc/backend/utils.go @@ -0,0 +1,281 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package backend + +import ( + "fmt" + "math/big" + "sort" + "strings" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/misc" + gethcore "github.com/ethereum/go-ethereum/core/types" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/log" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + + "github.com/cometbft/cometbft/proto/tendermint/crypto" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +type txGasAndReward struct { + gasUsed uint64 + reward *big.Int +} + +type sortGasAndReward []txGasAndReward + +func (s sortGasAndReward) Len() int { return len(s) } +func (s sortGasAndReward) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s sortGasAndReward) Less(i, j int) bool { + return s[i].reward.Cmp(s[j].reward) < 0 +} + +// getAccountNonce returns the account nonce for the given account address. +// If the pending value is true, it will iterate over the mempool (pending) +// txs in order to compute and return the pending tx sequence. +func (b *Backend) getAccountNonce(accAddr common.Address, pending bool, height int64, logger log.Logger) (uint64, error) { + queryClient := authtypes.NewQueryClient(b.clientCtx) + adr := sdk.AccAddress(accAddr.Bytes()).String() + ctx := rpc.NewContextWithHeight(height) + res, err := queryClient.Account(ctx, &authtypes.QueryAccountRequest{Address: adr}) + if err != nil { + st, ok := status.FromError(err) + // treat as account doesn't exist yet + if ok && st.Code() == codes.NotFound { + return 0, nil + } + return 0, err + } + var acc authtypes.AccountI + if err := b.clientCtx.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil { + return 0, err + } + + nonce := acc.GetSequence() + + if !pending { + return nonce, nil + } + + // the account retriever doesn't include the uncommitted transactions on the nonce, + // so we need to manually add them. + pendingTxs, err := b.PendingTransactions() + if err != nil { + logger.Error("failed to fetch pending transactions", "error", err.Error()) + return nonce, nil + } + + // add the uncommitted txs to the nonce counter + // only supports `MsgEthereumTx` style tx + for _, tx := range pendingTxs { + for _, msg := range (*tx).GetMsgs() { + ethMsg, ok := msg.(*evm.MsgEthereumTx) + if !ok { + // not ethereum tx + break + } + + sender, err := ethMsg.GetSender(b.chainID) + if err != nil { + continue + } + if sender == accAddr { + nonce++ + } + } + } + + return nonce, nil +} + +// retrieveEVMTxFeesFromBlock goes through evm txs of the block, +// retrieves the gas fees and puts them into an object `targetOneFeeHistory` +// See eth_feeHistory method for more details of the return format. +func (b *Backend) retrieveEVMTxFeesFromBlock( + tendermintBlock *tmrpctypes.ResultBlock, + ethBlock *map[string]any, + rewardPercentiles []float64, + tendermintBlockResult *tmrpctypes.ResultBlockResults, + targetOneFeeHistory *rpc.OneFeeHistory, +) error { + blockHeight := tendermintBlock.Block.Height + blockBaseFee, err := b.BaseFeeWei(tendermintBlockResult) + if err != nil { + return err + } + + // set basefee + targetOneFeeHistory.BaseFee = blockBaseFee + cfg := b.ChainConfig() + header, err := b.CurrentHeader() + if err != nil { + return err + } + targetOneFeeHistory.NextBaseFee = misc.CalcBaseFee(cfg, header) + + // set gas used ratio + gasLimitUint64, ok := (*ethBlock)["gasLimit"].(hexutil.Uint64) + if !ok { + return fmt.Errorf("invalid gas limit type: %T", (*ethBlock)["gasLimit"]) + } + + gasUsedBig, ok := (*ethBlock)["gasUsed"].(*hexutil.Big) + if !ok { + return fmt.Errorf("invalid gas used type: %T", (*ethBlock)["gasUsed"]) + } + + gasusedfloat, _ := new(big.Float).SetInt(gasUsedBig.ToInt()).Float64() + + if gasLimitUint64 <= 0 { + return fmt.Errorf("gasLimit of block height %d should be bigger than 0 , current gaslimit %d", blockHeight, gasLimitUint64) + } + + gasUsedRatio := gasusedfloat / float64(gasLimitUint64) + blockGasUsed := gasusedfloat + targetOneFeeHistory.GasUsedRatio = gasUsedRatio + + rewardCount := len(rewardPercentiles) + targetOneFeeHistory.Reward = make([]*big.Int, rewardCount) + for i := 0; i < rewardCount; i++ { + targetOneFeeHistory.Reward[i] = big.NewInt(0) + } + + // check tendermintTxs + tendermintTxs := tendermintBlock.Block.Txs + tendermintTxResults := tendermintBlockResult.TxsResults + tendermintTxCount := len(tendermintTxs) + + var sorter sortGasAndReward + + for i := 0; i < tendermintTxCount; i++ { + eachTendermintTx := tendermintTxs[i] + eachTendermintTxResult := tendermintTxResults[i] + + tx, err := b.clientCtx.TxConfig.TxDecoder()(eachTendermintTx) + if err != nil { + b.logger.Debug("failed to decode transaction in block", "height", blockHeight, "error", err.Error()) + continue + } + txGasUsed := uint64(eachTendermintTxResult.GasUsed) // #nosec G701 + for _, msg := range tx.GetMsgs() { + ethMsg, ok := msg.(*evm.MsgEthereumTx) + if !ok { + continue + } + tx := ethMsg.AsTransaction() + reward := tx.EffectiveGasTipValue(blockBaseFee) + if reward == nil { + reward = big.NewInt(0) + } + sorter = append(sorter, txGasAndReward{gasUsed: txGasUsed, reward: reward}) + } + } + + // return an all zero row if there are no transactions to gather data from + ethTxCount := len(sorter) + if ethTxCount == 0 { + return nil + } + + sort.Sort(sorter) + + var txIndex int + sumGasUsed := sorter[0].gasUsed + + for i, p := range rewardPercentiles { + thresholdGasUsed := uint64(blockGasUsed * p / 100) // #nosec G701 + for sumGasUsed < thresholdGasUsed && txIndex < ethTxCount-1 { + txIndex++ + sumGasUsed += sorter[txIndex].gasUsed + } + targetOneFeeHistory.Reward[i] = sorter[txIndex].reward + } + + return nil +} + +// TxLogsFromEvents parses ethereum logs from cosmos events for specific msg index +func TxLogsFromEvents(events []abci.Event, msgIndex int) ([]*gethcore.Log, error) { + for _, event := range events { + if event.Type != evm.TypeUrlEventTxLog { + continue + } + + if msgIndex > 0 { + // not the eth tx we want + msgIndex-- + continue + } + + eventTxLog, err := evm.EventTxLogFromABCIEvent(event) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse event tx log") + } + return evm.LogsToEthereum(eventTxLog.Logs), nil + } + return nil, fmt.Errorf("eth tx logs not found for message index %d", msgIndex) +} + +// ShouldIgnoreGasUsed returns true if the gasUsed in result should be ignored +// workaround for issue: https://github.com/cosmos/cosmos-sdk/issues/10832 +func ShouldIgnoreGasUsed(res *abci.ResponseDeliverTx) bool { + return res.GetCode() == 11 && strings.Contains(res.GetLog(), "no block gas left to run tx: out of gas") +} + +// GetLogsFromBlockResults returns the list of event logs from the tendermint block result response +func GetLogsFromBlockResults( + blockRes *tmrpctypes.ResultBlockResults, +) (blockLogs [][]*gethcore.Log, err error) { + blockLogs = [][]*gethcore.Log{} + for _, txResult := range blockRes.TxsResults { + txLogs := []*gethcore.Log{} + + for _, event := range txResult.Events { + if event.Type != evm.TypeUrlEventTxLog { + continue + } + + eventTxLogs, err := evm.EventTxLogFromABCIEvent(event) + if err != nil { + return nil, err + } + + txLogs = append(txLogs, evm.LogsToEthereum(eventTxLogs.Logs)...) + } + + blockLogs = append(blockLogs, txLogs) + } + + return blockLogs, nil +} + +// GetHexProofs returns list of hex data of proof op +func GetHexProofs(proofOps *crypto.ProofOps) []string { + if proofOps == nil { + return []string{""} + } + proofs := []string{} + // check for proof + for _, p := range proofOps.Ops { + proof := "" + if len(p.Data) > 0 { + proof = hexutil.Encode(p.Data) + } + proofs = append(proofs, proof) + } + return proofs +} diff --git a/eth/rpc/backend/utils_test.go b/eth/rpc/backend/utils_test.go new file mode 100644 index 000000000..083d24361 --- /dev/null +++ b/eth/rpc/backend/utils_test.go @@ -0,0 +1,80 @@ +package backend_test + +import ( + "fmt" + + "github.com/cometbft/cometbft/proto/tendermint/crypto" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + gethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" +) + +func (s *BackendSuite) TestGetLogsFromBlockResults() { + blockWithTx := deployContractBlockNumber.Int64() + blockResults, err := s.backend.TendermintBlockResultByNumber(&blockWithTx) + s.Require().NoError(err) + s.Require().NotNil(blockResults) + + logs, err := backend.GetLogsFromBlockResults(blockResults) + s.Require().NoError(err) + s.Require().NotNil(logs) + + s.assertTxLogsMatch([]*gethcore.Log{ + { + Address: testContractAddress, + Topics: []gethcommon.Hash{ + gethcrypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")), + gethcommon.Address{}.Hash(), + s.fundedAccEthAddr.Hash(), + }, + }, + }, logs[0], "deploy contract tx") +} + +func (s *BackendSuite) TestGetHexProofs() { + defaultRes := []string{""} + testCases := []struct { + name string + proof *crypto.ProofOps + exp []string + }{ + { + "no proof provided", + mockProofs(0, false), + defaultRes, + }, + { + "no proof data provided", + mockProofs(1, false), + defaultRes, + }, + { + "valid proof provided", + mockProofs(1, true), + []string{"0x0a190a034b4559120556414c55451a0b0801180120012a03000202"}, + }, + } + for _, tc := range testCases { + s.Run(fmt.Sprintf("Case %s", tc.name), func() { + s.Require().Equal(tc.exp, backend.GetHexProofs(tc.proof)) + }) + } +} + +func mockProofs(num int, withData bool) *crypto.ProofOps { + var proofOps *crypto.ProofOps + if num > 0 { + proofOps = new(crypto.ProofOps) + for i := 0; i < num; i++ { + proof := crypto.ProofOp{} + if withData { + proof.Data = []byte("\n\031\n\003KEY\022\005VALUE\032\013\010\001\030\001 \001*\003\000\002\002") + } + proofOps.Ops = append(proofOps.Ops, proof) + } + } + return proofOps +} diff --git a/eth/rpc/block.go b/eth/rpc/block.go new file mode 100644 index 000000000..0d456455e --- /dev/null +++ b/eth/rpc/block.go @@ -0,0 +1,203 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpc + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "math" + "math/big" + "strings" + + "github.com/spf13/cast" + "google.golang.org/grpc/metadata" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// BlockNumber represents a decoded hex string for a block number. +type BlockNumber int64 + +const ( + EthPendingBlockNumber = BlockNumber(-2) + EthLatestBlockNumber = BlockNumber(-1) + EthEarliestBlockNumber = BlockNumber(0) +) + +const ( + BlockParamEarliest = "earliest" + BlockParamLatest = "latest" + BlockParamFinalized = "finalized" + BlockParamSafe = "safe" + BlockParamPending = "pending" +) + +// NewBlockNumber creates a new BlockNumber instance. +func NewBlockNumber(n *big.Int) BlockNumber { + if !n.IsInt64() { + // default to latest block if it overflows + return EthLatestBlockNumber + } + + return BlockNumber(n.Int64()) +} + +// NewContextWithHeight wraps a context with the a gRPC block height header. If the +// provided height is 0, it will return an empty context and the gRPC query will +// use the latest block height for querying. Note that all metadata gets processed +// and removed by tendermint layer, so it wont be accessible at gRPC server +// level. +func NewContextWithHeight(height int64) context.Context { + if height == 0 { + return context.Background() + } + return metadata.AppendToOutgoingContext( + context.Background(), + grpctypes.GRPCBlockHeightHeader, + fmt.Sprintf("%d", height), + ) +} + +// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: +// - "latest", "finalized", "earliest" or "pending" as string arguments +// - the block number +// +// Returned errors: +// - Invalid block number error when the given argument isn't a known string +// - Out of range error when the given block number is too large or small +func (bn *BlockNumber) UnmarshalJSON(data []byte) error { + input := strings.TrimSpace(string(data)) + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] + } + + switch input { + case BlockParamEarliest: + *bn = EthEarliestBlockNumber + return nil + case BlockParamLatest, BlockParamFinalized, BlockParamSafe: + *bn = EthLatestBlockNumber + return nil + case BlockParamPending: + *bn = EthPendingBlockNumber + return nil + } + + blckNum, err := hexutil.DecodeUint64(input) + if errors.Is(err, hexutil.ErrMissingPrefix) { + blckNum = cast.ToUint64(input) + } else if err != nil { + return err + } + + if blckNum > math.MaxInt64 { + return fmt.Errorf("block number larger than int64") + } + *bn = BlockNumber(blckNum) // #nosec G701 + + return nil +} + +// Int64 converts block number to primitive type. This function enforces the +// first block starting from 1-index. +func (bn BlockNumber) Int64() int64 { + if bn < 0 { + return 0 + } else if bn == 0 { + return 1 + } + + return int64(bn) +} + +// TmHeight is a util function used for the Tendermint RPC client. It returns +// nil if the block number is "latest". Otherwise, it returns the pointer of the +// int64 value of the height. +func (bn BlockNumber) TmHeight() *int64 { + if bn < 0 { + return nil + } + + height := bn.Int64() + return &height +} + +// BlockNumberOrHash represents a block number or a block hash. +type BlockNumberOrHash struct { + BlockNumber *BlockNumber `json:"blockNumber,omitempty"` + BlockHash *common.Hash `json:"blockHash,omitempty"` +} + +func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { + type erased BlockNumberOrHash + e := erased{} + err := json.Unmarshal(data, &e) + if err == nil { + return bnh.checkUnmarshal(BlockNumberOrHash(e)) + } + var input string + err = json.Unmarshal(data, &input) + if err != nil { + return err + } + err = bnh.decodeFromString(input) + if err != nil { + return err + } + + return nil +} + +func (bnh *BlockNumberOrHash) checkUnmarshal(e BlockNumberOrHash) error { + if e.BlockNumber != nil && e.BlockHash != nil { + return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other") + } + bnh.BlockNumber = e.BlockNumber + bnh.BlockHash = e.BlockHash + return nil +} + +func (bnh *BlockNumberOrHash) decodeFromString(input string) error { + switch input { + case BlockParamEarliest: + bn := EthEarliestBlockNumber + bnh.BlockNumber = &bn + case BlockParamLatest, BlockParamFinalized: + bn := EthLatestBlockNumber + bnh.BlockNumber = &bn + case BlockParamPending: + bn := EthPendingBlockNumber + bnh.BlockNumber = &bn + default: + // check if the input is a block hash + if len(input) == 66 { + hash := common.Hash{} + err := hash.UnmarshalText([]byte(input)) + if err != nil { + return err + } + bnh.BlockHash = &hash + break + } + // otherwise take the hex string has int64 value + blockNumber, err := hexutil.DecodeUint64(input) + if err != nil { + return err + } + + bnInt, err := eth.SafeInt64(blockNumber) + if err != nil { + return err + } + + bn := BlockNumber(bnInt) + bnh.BlockNumber = &bn + } + return nil +} diff --git a/eth/rpc/block_test.go b/eth/rpc/block_test.go new file mode 100644 index 000000000..1fd502272 --- /dev/null +++ b/eth/rpc/block_test.go @@ -0,0 +1,144 @@ +package rpc + +import ( + "fmt" + "math/big" + "testing" + + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/ethereum/go-ethereum/common" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc/metadata" +) + +type BlockSuite struct { + suite.Suite +} + +func TestBlockSuite(t *testing.T) { + suite.Run(t, new(BlockSuite)) +} + +func (s *BlockSuite) TestNewBlockNumber() { + bigInt := big.NewInt(1) + bn := NewBlockNumber(bigInt) + bnInt64 := bn.Int64() + bnTmHeight := bn.TmHeight() + s.EqualValues(bnInt64, *bnTmHeight) + s.EqualValues(bigInt.Int64(), bnInt64) +} + +func (s *BlockSuite) TestNewContextWithHeight() { + // Test with zero height + ctxZero := NewContextWithHeight(0) + _, ok := metadata.FromOutgoingContext(ctxZero) + s.False(ok, "No metadata should be present for height 0") + + // Test with non-zero height + height := int64(10) + ctxTen := NewContextWithHeight(height) + md, ok := metadata.FromOutgoingContext(ctxTen) + s.True(ok, "Metadata should be present for non-zero height") + s.NotEmpty(md, "Metadata should not be empty") + + heightStr, ok := md[grpctypes.GRPCBlockHeightHeader] + s.True(ok, grpctypes.GRPCBlockHeightHeader, " metadata should be present") + s.Require().Len(heightStr, 1, + fmt.Sprintf("There should be exactly one %s value", grpctypes.GRPCBlockHeightHeader)) + s.Equal(fmt.Sprintf("%d", height), heightStr[0], + "The height value in metadata should match the provided height") +} + +func (s *BlockSuite) TestUnmarshalBlockNumberOrHash() { + bnh := new(BlockNumberOrHash) + + testCases := []struct { + msg string + input []byte + malleate func() + expPass bool + }{ + { + msg: "JSON input with block hash", + input: []byte("{\"blockHash\": \"0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739\"}"), + malleate: func() { + s.Equal(*bnh.BlockHash, common.HexToHash("0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739")) + s.Nil(bnh.BlockNumber) + }, + expPass: true, + }, + { + "JSON input with block number", + []byte("{\"blockNumber\": \"0x35\"}"), + func() { + s.Equal(*bnh.BlockNumber, BlockNumber(0x35)) + s.Nil(bnh.BlockHash) + }, + true, + }, + { + "JSON input with block number latest", + []byte("{\"blockNumber\": \"latest\"}"), + func() { + s.Equal(*bnh.BlockNumber, EthLatestBlockNumber) + s.Nil(bnh.BlockHash) + }, + true, + }, + { + "JSON input with both block hash and block number", + []byte("{\"blockHash\": \"0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739\", \"blockNumber\": \"0x35\"}"), + func() { + }, + false, + }, + { + "String input with block hash", + []byte("\"0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739\""), + func() { + s.Equal(*bnh.BlockHash, common.HexToHash("0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739")) + s.Nil(bnh.BlockNumber) + }, + true, + }, + { + "String input with block number", + []byte("\"0x35\""), + func() { + s.Equal(*bnh.BlockNumber, BlockNumber(0x35)) + s.Nil(bnh.BlockHash) + }, + true, + }, + { + "String input with block number latest", + []byte("\"latest\""), + func() { + s.Equal(*bnh.BlockNumber, EthLatestBlockNumber) + s.Nil(bnh.BlockHash) + }, + true, + }, + { + "String input with block number overflow", + []byte("\"0xffffffffffffffffffffffffffffffffffffff\""), + func() { + }, + false, + }, + } + + for _, tc := range testCases { + log.Info().Msgf("Case %s", tc.msg) + // reset input + bnh = new(BlockNumberOrHash) + err := bnh.UnmarshalJSON(tc.input) + tc.malleate() + if tc.expPass { + s.NoError(err) + } else { + s.Error(err) + } + } +} diff --git a/eth/rpc/events_parser.go b/eth/rpc/events_parser.go new file mode 100644 index 000000000..ea15f025f --- /dev/null +++ b/eth/rpc/events_parser.go @@ -0,0 +1,172 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpc + +import ( + "errors" + "fmt" + "strconv" + + abci "github.com/cometbft/cometbft/abci/types" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// ParsedTx is eth tx info parsed from ABCI events. Each `ParsedTx` corresponds +// to one eth tx msg ([evm.MsgEthereumTx]). +type ParsedTx struct { + MsgIndex int + + // the following fields are parsed from events + EthHash gethcommon.Hash + + EthTxIndex int32 // -1 means uninitialized + GasUsed uint64 + Failed bool +} + +// ParsedTxs is the tx infos parsed from eth tx events. +type ParsedTxs struct { + // one item per message + Txs []ParsedTx + // map tx hash to msg index + TxHashes map[gethcommon.Hash]int +} + +// ParseTxResult parses eth tx info from the ABCI events of Eth tx msgs +func ParseTxResult(result *abci.ResponseDeliverTx, tx sdk.Tx) (*ParsedTxs, error) { + // Parsed txs is the structure being populated from the events + // So far (until we allow ethereum_txs as cosmos tx messages) it'll have single tx + parsedTxs := &ParsedTxs{ + Txs: make([]ParsedTx, 0), + TxHashes: make(map[gethcommon.Hash]int), + } + + // msgIndex counts only ethereum tx messages. + msgIndex := -1 + for _, event := range result.Events { + // Pending tx event could be single if tx didn't succeed + if event.Type == evm.PendingEthereumTxEvent { + msgIndex++ + ethHash, txIndex, err := evm.GetEthHashAndIndexFromPendingEthereumTxEvent(event) + if err != nil { + return nil, err + } + pendingTx := ParsedTx{ + MsgIndex: msgIndex, + EthTxIndex: txIndex, + EthHash: ethHash, + } + parsedTxs.Txs = append(parsedTxs.Txs, pendingTx) + parsedTxs.TxHashes[ethHash] = msgIndex + } else if event.Type == evm.TypeUrlEventEthereumTx { // Full event replaces the pending tx + eventEthereumTx, err := evm.EventEthereumTxFromABCIEvent(event) + if err != nil { + return nil, err + } + ethTxIndexFromEvent, err := strconv.ParseUint(eventEthereumTx.Index, 10, 31) + if err != nil { + return nil, fmt.Errorf("failed to parse EthTxIndex from event: %w", err) + } + gasUsed, err := strconv.ParseUint(eventEthereumTx.GasUsed, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse GasUsed from event: %w", err) + } + committedTx := ParsedTx{ + MsgIndex: msgIndex, + EthTxIndex: int32(ethTxIndexFromEvent), + EthHash: gethcommon.HexToHash(eventEthereumTx.EthHash), + GasUsed: gasUsed, + Failed: len(eventEthereumTx.VmError) > 0, + } + // find the pending tx to replace by tx hash + pendingMsgIndex, found := parsedTxs.TxHashes[committedTx.EthHash] + if found { + parsedTxs.Txs[pendingMsgIndex] = committedTx + } else { + return nil, errors.New("EventEthereumTx without pending_ethereum_tx event") + } + } + } + + // this could only happen if tx exceeds block gas limit + if result.Code != 0 && tx != nil { + for i := 0; i < len(parsedTxs.Txs); i++ { + parsedTxs.Txs[i].Failed = true + + // replace gasUsed with gasLimit because that's what's actually deducted. + msgEthereumTx, ok := tx.GetMsgs()[i].(*evm.MsgEthereumTx) + if !ok { + return nil, fmt.Errorf("unexpected message type at index %d", i) + } + gasLimit := msgEthereumTx.GetGas() + parsedTxs.Txs[i].GasUsed = gasLimit + } + } + return parsedTxs, nil +} + +// ParseTxIndexerResult parse tm tx result to a format compatible with the custom tx indexer. +func ParseTxIndexerResult(txResult *tmrpctypes.ResultTx, tx sdk.Tx, getter func(*ParsedTxs) *ParsedTx) (*eth.TxResult, error) { + txs, err := ParseTxResult(&txResult.TxResult, tx) + if err != nil { + return nil, fmt.Errorf("failed to parse tx events: block %d, index %d, %v", txResult.Height, txResult.Index, err) + } + + parsedTx := getter(txs) + if parsedTx == nil { + return nil, fmt.Errorf("ethereum tx not found in msgs: block %d, index %d", txResult.Height, txResult.Index) + } + index := uint32(parsedTx.MsgIndex) // #nosec G701 + return ð.TxResult{ + Height: txResult.Height, + TxIndex: txResult.Index, + MsgIndex: index, + EthTxIndex: parsedTx.EthTxIndex, + Failed: parsedTx.Failed, + GasUsed: parsedTx.GasUsed, + CumulativeGasUsed: txs.AccumulativeGasUsed(parsedTx.MsgIndex), + }, nil +} + +// GetTxByHash find ParsedTx by tx hash, returns nil if not exists. +func (p *ParsedTxs) GetTxByHash(hash gethcommon.Hash) *ParsedTx { + if idx, ok := p.TxHashes[hash]; ok { + return &p.Txs[idx] + } + return nil +} + +// GetTxByMsgIndex returns ParsedTx by msg index +func (p *ParsedTxs) GetTxByMsgIndex(i int) *ParsedTx { + if i < 0 || i >= len(p.Txs) { + return nil + } + return &p.Txs[i] +} + +// GetTxByTxIndex returns ParsedTx by tx index +func (p *ParsedTxs) GetTxByTxIndex(txIndex int) *ParsedTx { + if len(p.Txs) == 0 { + return nil + } + // assuming the `EthTxIndex` increase continuously, + // convert TxIndex to MsgIndex by subtract the begin TxIndex. + msgIndex := txIndex - int(p.Txs[0].EthTxIndex) + // GetTxByMsgIndex will check the bound + return p.GetTxByMsgIndex(msgIndex) +} + +// AccumulativeGasUsed calculates the accumulated gas used within the batch of txs +func (p *ParsedTxs) AccumulativeGasUsed(msgIndex int) (result uint64) { + if msgIndex < 0 || msgIndex >= len(p.Txs) { + return 0 + } + for i := 0; i <= msgIndex; i++ { + result += p.Txs[i].GasUsed + } + return result +} diff --git a/eth/rpc/events_parser_test.go b/eth/rpc/events_parser_test.go new file mode 100644 index 000000000..91a891394 --- /dev/null +++ b/eth/rpc/events_parser_test.go @@ -0,0 +1,201 @@ +package rpc + +import ( + "math/big" + "strconv" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +func TestParseTxResult(t *testing.T) { + txHashOne := gethcommon.BigToHash(big.NewInt(1)) + txHashTwo := gethcommon.BigToHash(big.NewInt(2)) + + type TestCase struct { + name string + txResp abci.ResponseDeliverTx + wantEthTxs []*ParsedTx + wantErr bool + } + + testCases := []TestCase{ + { + name: "happy: valid single pending_ethereum_tx event", + txResp: abci.ResponseDeliverTx{ + Events: []abci.Event{ + pendingEthereumTxEvent(txHashOne.Hex(), 0), + }, + }, + wantEthTxs: []*ParsedTx{ + { + MsgIndex: 0, + EthHash: txHashOne, + EthTxIndex: 0, + GasUsed: 0, + Failed: false, + }, + }, + }, + { + name: "happy: two valid pending_ethereum_tx events", + txResp: abci.ResponseDeliverTx{ + Events: []abci.Event{ + pendingEthereumTxEvent(txHashOne.Hex(), 0), + pendingEthereumTxEvent(txHashTwo.Hex(), 1), + }, + }, + wantEthTxs: []*ParsedTx{ + { + MsgIndex: 0, + EthHash: txHashOne, + EthTxIndex: 0, + GasUsed: 0, + Failed: false, + }, + { + MsgIndex: 1, + EthHash: txHashTwo, + EthTxIndex: 1, + Failed: false, + }, + }, + }, + { + name: "happy: one pending_ethereum_tx and one EventEthereumTx", + txResp: abci.ResponseDeliverTx{ + Events: []abci.Event{ + pendingEthereumTxEvent(txHashOne.Hex(), 0), + ethereumTxEvent(txHashOne.Hex(), 0, 21000, false), + }, + }, + wantEthTxs: []*ParsedTx{ + { + MsgIndex: 0, + EthHash: txHashOne, + EthTxIndex: 0, + GasUsed: 21000, + Failed: false, + }, + }, + }, + { + name: "happy: two pending_ethereum_tx and one EventEthereumTx", + txResp: abci.ResponseDeliverTx{ + Events: []abci.Event{ + pendingEthereumTxEvent(txHashOne.Hex(), 0), + pendingEthereumTxEvent(txHashTwo.Hex(), 1), + ethereumTxEvent(txHashTwo.Hex(), 1, 21000, false), + }, + }, + wantEthTxs: []*ParsedTx{ + { + MsgIndex: 0, + EthHash: txHashOne, + EthTxIndex: 0, + GasUsed: 0, + Failed: false, + }, + { + MsgIndex: 1, + EthHash: txHashTwo, + EthTxIndex: 1, + GasUsed: 21000, + Failed: false, + }, + }, + }, + { + name: "happy: one pending_ethereum_tx and one EventEthereumTx failed", + txResp: abci.ResponseDeliverTx{ + Events: []abci.Event{ + pendingEthereumTxEvent(txHashOne.Hex(), 0), + ethereumTxEvent(txHashOne.Hex(), 0, 21000, true), + }, + }, + wantEthTxs: []*ParsedTx{ + { + MsgIndex: 0, + EthHash: txHashOne, + EthTxIndex: 0, + GasUsed: 21000, + Failed: true, + }, + }, + }, + { + name: "sad: EventEthereumTx without pending_ethereum_tx", + txResp: abci.ResponseDeliverTx{ + Events: []abci.Event{ + ethereumTxEvent(txHashOne.Hex(), 0, 21000, false), + }, + }, + wantEthTxs: nil, + wantErr: true, + }, + { + name: "sad: no events", + txResp: abci.ResponseDeliverTx{ + Events: []abci.Event{}, + }, + wantEthTxs: []*ParsedTx{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + parsed, err := ParseTxResult(&tc.txResp, nil) //#nosec G601 -- fine for tests + if tc.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + for msgIndex, expTx := range tc.wantEthTxs { + require.Equal(t, expTx, parsed.GetTxByMsgIndex(msgIndex)) + require.Equal(t, expTx, parsed.GetTxByHash(expTx.EthHash)) + require.Equal(t, expTx, parsed.GetTxByTxIndex(int(expTx.EthTxIndex))) + require.Equal(t, expTx.GasUsed, parsed.GetTxByHash(expTx.EthHash).GasUsed) + require.Equal(t, expTx.Failed, parsed.GetTxByHash(expTx.EthHash).Failed) + } + // non-exists tx hash + require.Nil(t, parsed.GetTxByHash(gethcommon.Hash{})) + // out of range + require.Nil(t, parsed.GetTxByMsgIndex(len(tc.wantEthTxs))) + require.Nil(t, parsed.GetTxByTxIndex(99999999)) + }) + } +} + +func pendingEthereumTxEvent(txHash string, txIndex int) abci.Event { + return abci.Event{ + Type: evm.PendingEthereumTxEvent, + Attributes: []abci.EventAttribute{ + {Key: evm.PendingEthereumTxEventAttrEthHash, Value: txHash}, + {Key: evm.PendingEthereumTxEventAttrIndex, Value: strconv.Itoa(txIndex)}, + }, + } +} + +func ethereumTxEvent(txHash string, txIndex int, gasUsed int, failed bool) abci.Event { + var vmError string + if failed { + vmError = "failed" + } + event, err := sdk.TypedEventToEvent( + &evm.EventEthereumTx{ + EthHash: txHash, + Index: strconv.Itoa(txIndex), + GasUsed: strconv.Itoa(gasUsed), + VmError: vmError, + }, + ) + if err != nil { + panic(err) + } + return (abci.Event)(event) +} diff --git a/eth/rpc/pubsub/pubsub.go b/eth/rpc/pubsub/pubsub.go new file mode 100644 index 000000000..f5baabd54 --- /dev/null +++ b/eth/rpc/pubsub/pubsub.go @@ -0,0 +1,174 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package pubsub + +import ( + "sync" + "sync/atomic" + + "github.com/pkg/errors" + + coretypes "github.com/cometbft/cometbft/rpc/core/types" +) + +type UnsubscribeFunc func() + +// EventBus manages topics and subscriptions. A "topic" is a named channel of +// communication. A "subscription" is the action taken by a subscriber to express +// interest in receiving messages broadcasted from a specific topic. +type EventBus interface { + // AddTopic: Adds a new topic with the specified name and message source + AddTopic(name string, src <-chan coretypes.ResultEvent) error + // RemoveTopic: Removes the specified topic and all its related data, + // ensuring clean up of resources. + RemoveTopic(name string) + Subscribe(name string) (<-chan coretypes.ResultEvent, UnsubscribeFunc, error) + Topics() []string +} + +// memEventBus is an implemention of the `EventBus` interface. +type memEventBus struct { + topics map[string]<-chan coretypes.ResultEvent + topicsMux *sync.RWMutex + subscribers map[string]map[uint64]chan<- coretypes.ResultEvent + subscribersMux *sync.RWMutex + currentUniqueID uint64 +} + +// NewEventBus returns a fresh imlpemention of `memEventBus`, which implements +// the `EventBus` interface for managing Ethereum topics and subscriptions. +func NewEventBus() EventBus { + return &memEventBus{ + topics: make(map[string]<-chan coretypes.ResultEvent), + topicsMux: new(sync.RWMutex), + subscribers: make(map[string]map[uint64]chan<- coretypes.ResultEvent), + subscribersMux: new(sync.RWMutex), + } +} + +// GenUniqueID atomically increments and returns a unique identifier for a new subscriber. +// This ID is used internally to manage subscriber-specific channels. +func (m *memEventBus) GenUniqueID() uint64 { + return atomic.AddUint64(&m.currentUniqueID, 1) +} + +// Topics returns a list of all topics currently managed by the EventBus. The +// list is safe for concurrent access and is a snapshot of current topic names. +func (m *memEventBus) Topics() (topics []string) { + m.topicsMux.RLock() + defer m.topicsMux.RUnlock() + + topics = make([]string, 0, len(m.topics)) + for topicName := range m.topics { + topics = append(topics, topicName) + } + + return topics +} + +// AddTopic adds a new topic with the specified name and message source +func (m *memEventBus) AddTopic(name string, src <-chan coretypes.ResultEvent) error { + m.topicsMux.RLock() + _, ok := m.topics[name] + m.topicsMux.RUnlock() + + if ok { + return errors.New("topic already registered") + } + + m.topicsMux.Lock() + m.topics[name] = src + m.topicsMux.Unlock() + + go m.publishTopic(name, src) + + return nil +} + +// RemoveTopic: Removes the specified topic and all its related data, ensuring +// clean up of resources. +func (m *memEventBus) RemoveTopic(name string) { + m.topicsMux.Lock() + delete(m.topics, name) + m.topicsMux.Unlock() +} + +// Subscribe attempts to create a subscription to the specified topic. It returns +// a channel to receive messages, a function to unsubscribe, and an error if the +// topic does not exist. +func (m *memEventBus) Subscribe(name string) (<-chan coretypes.ResultEvent, UnsubscribeFunc, error) { + m.topicsMux.RLock() + _, ok := m.topics[name] + m.topicsMux.RUnlock() + + if !ok { + return nil, nil, errors.Errorf("topic not found: %s", name) + } + + ch := make(chan coretypes.ResultEvent) + m.subscribersMux.Lock() + defer m.subscribersMux.Unlock() + + id := m.GenUniqueID() + if _, ok := m.subscribers[name]; !ok { + m.subscribers[name] = make(map[uint64]chan<- coretypes.ResultEvent) + } + m.subscribers[name][id] = ch + + unsubscribe := func() { + m.subscribersMux.Lock() + defer m.subscribersMux.Unlock() + delete(m.subscribers[name], id) + } + + return ch, unsubscribe, nil +} + +func (m *memEventBus) publishTopic(name string, src <-chan coretypes.ResultEvent) { + for { + msg, ok := <-src + if !ok { + m.closeAllSubscribers(name) + m.topicsMux.Lock() + delete(m.topics, name) + m.topicsMux.Unlock() + return + } + m.publishAllSubscribers(name, msg) + } +} + +// closeAllSubscribers closes all subscriber channels associated with the +// specified topic and removes the topic from the subscribers map. This function +// is typically called when a topic is deleted or no longer available to ensure +// all resources are released properly and to prevent goroutine leaks. It ensures +// thread-safe execution by locking around the operation. +func (m *memEventBus) closeAllSubscribers(name string) { + m.subscribersMux.Lock() + defer m.subscribersMux.Unlock() + + subscribers := m.subscribers[name] + delete(m.subscribers, name) + // #nosec G705 + for _, sub := range subscribers { + close(sub) + } +} + +// publishAllSubscribers sends a message to all subscribers of the specified +// topic. It uses a non-blocking send operation to deliver the message to +// subscriber channels. If a subscriber's channel is not ready to receive the +// message (i.e., the channel is full), the message is skipped for that +// subscriber to avoid blocking the publisher. This function ensures thread-safe +// access to subscribers by using a read lock. +func (m *memEventBus) publishAllSubscribers(name string, msg coretypes.ResultEvent) { + m.subscribersMux.RLock() + defer m.subscribersMux.RUnlock() + subscribers := m.subscribers[name] + // #nosec G705 + for _, sub := range subscribers { + select { + case sub <- msg: + default: + } + } +} diff --git a/eth/rpc/pubsub/pubsub_test.go b/eth/rpc/pubsub/pubsub_test.go new file mode 100644 index 000000000..e03de763d --- /dev/null +++ b/eth/rpc/pubsub/pubsub_test.go @@ -0,0 +1,155 @@ +package pubsub + +import ( + "log" + "sort" + "sync" + "testing" + "time" + + rpccore "github.com/cometbft/cometbft/rpc/core/types" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +// subscribeAndPublish: Helper function used to perform concurrent subscription +// and publishing actions. It concurrently subscribes multiple clients to the +// specified topic and simultanesouly sends an empty message to the topic channel +// for each subscription. +func subscribeAndPublish(t *testing.T, eb EventBus, topic string, topicChan chan rpccore.ResultEvent) { + var ( + wg sync.WaitGroup + subscribersCount = 50 + emptyMsg = rpccore.ResultEvent{} + ) + for i := 0; i < subscribersCount; i++ { + wg.Add(1) + // concurrently subscribe to the topic + go func() { + defer wg.Done() + _, _, err := eb.Subscribe(topic) + require.NoError(t, err) + }() + + // send events to the topic + wg.Add(1) + go func() { + defer wg.Done() + topicChan <- emptyMsg + }() + } + wg.Wait() +} + +type SuitePubsub struct { + suite.Suite +} + +func TestSuitePubsub(t *testing.T) { + suite.Run(t, new(SuitePubsub)) +} + +func (s *SuitePubsub) TestAddTopic() { + q := NewEventBus() + // dummy vars + topicA := "guard" + topicB := "cream" + + s.NoError(q.AddTopic(topicA, make(<-chan rpccore.ResultEvent))) + s.NoError(q.AddTopic(topicB, make(<-chan rpccore.ResultEvent))) + s.Error(q.AddTopic(topicB, make(<-chan rpccore.ResultEvent))) + + topics := q.Topics() + sort.Strings(topics) // cream should be first + s.Require().EqualValues([]string{topicB, topicA}, topics) +} + +func (s *SuitePubsub) TestSubscribe() { + q := NewEventBus() + + // dummy vars + topicA := "0xfoo" + topicB := "blockchain" + + srcA := make(chan rpccore.ResultEvent) + err := q.AddTopic(topicA, srcA) + s.NoError(err) + + srcB := make(chan rpccore.ResultEvent) + err = q.AddTopic(topicB, srcB) + s.NoError(err) + + // subscriber channels + subChanA, _, err := q.Subscribe(topicA) + s.NoError(err) + subChanB1, _, err := q.Subscribe(topicB) + s.NoError(err) + subChanB2, _, err := q.Subscribe(topicB) + s.NoError(err) + + wg := new(sync.WaitGroup) + wg.Add(4) + + emptyMsg := rpccore.ResultEvent{} + go func() { + defer wg.Done() + msg := <-subChanA + log.Println(topicA+":", msg) + s.EqualValues(emptyMsg, msg) + }() + + go func() { + defer wg.Done() + msg := <-subChanB1 + log.Println(topicB+":", msg) + s.EqualValues(emptyMsg, msg) + }() + + go func() { + defer wg.Done() + msg := <-subChanB2 + log.Println(topicB+"2:", msg) + s.EqualValues(emptyMsg, msg) + }() + + go func() { + defer wg.Done() + + time.Sleep(time.Second) + + close(srcA) + close(srcB) + }() + + wg.Wait() + time.Sleep(time.Second) +} + +// TestConcurrentSubscribeAndPublish: Stress tests the module to make sure that +// operations are handled properly under concurrent access. +func (s *SuitePubsub) TestConcurrentSubscribeAndPublish() { + var ( + wg sync.WaitGroup + eb = NewEventBus() + topicName = "topic-name" + topicCh = make(chan rpccore.ResultEvent) + runsCount = 5 + ) + + err := eb.AddTopic(topicName, topicCh) + s.Require().NoError(err) + + for i := 0; i < runsCount; i++ { + subscribeAndPublish(s.T(), eb, topicName, topicCh) + } + + // close channel to make test end + wg.Add(1) + go func() { + defer wg.Done() + time.Sleep(2 * time.Second) + close(topicCh) + }() + + wg.Wait() +} diff --git a/eth/rpc/query_client.go b/eth/rpc/query_client.go new file mode 100644 index 000000000..e55be81a3 --- /dev/null +++ b/eth/rpc/query_client.go @@ -0,0 +1,71 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpc + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/types/tx" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/proto/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/client" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// QueryClient defines a gRPC Client used for: +// - TM transaction simulation +// - EVM module queries +type QueryClient struct { + tx.ServiceClient + evm.QueryClient +} + +// NewQueryClient creates a new gRPC query client +// +// TODO:🔗 https://github.com/NibiruChain/nibiru/issues/1857 +// test(eth): Test GetProof (rpc/types/query_client.go) in a similar manner to +// cosmos-sdk/client/rpc/rpc_test.go using a network after EVM is wired into the +// app keepers: +func NewQueryClient(clientCtx client.Context) *QueryClient { + return &QueryClient{ + ServiceClient: tx.NewServiceClient(clientCtx), + QueryClient: evm.NewQueryClient(clientCtx), + } +} + +// GetProof performs an ABCI query with the given key and returns a merkle proof. The desired +// tendermint height to perform the query should be set in the client context. The query will be +// performed at one below this height (at the IAVL version) in order to obtain the correct merkle +// proof. Proof queries at height less than or equal to 2 are not supported. +// Issue: https://github.com/cosmos/cosmos-sdk/issues/6567 +func (QueryClient) GetProof( + clientCtx client.Context, storeKey string, key []byte, +) ([]byte, *crypto.ProofOps, error) { + height := clientCtx.Height + // ABCI queries at height less than or equal to 2 are not supported. + // Base app does not support queries for height less than or equal to 1, and + // the base app uses 0 indexing. + // + // Ethereum uses 1 indexing for the initial block height, therefore <= 2 means + // <= (Eth) height 3. + if height <= 2 { + return nil, nil, fmt.Errorf( + "proof queries at ABCI block height <= 2 are not supported") + } + + abciReq := abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", storeKey), + Data: key, + Height: height, + Prove: true, + } + + abciRes, err := clientCtx.QueryABCI(abciReq) + if err != nil { + return nil, nil, err + } + + return abciRes.Value, abciRes.ProofOps, nil +} diff --git a/eth/rpc/rpc.go b/eth/rpc/rpc.go new file mode 100644 index 000000000..c6c0891de --- /dev/null +++ b/eth/rpc/rpc.go @@ -0,0 +1,277 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpc + +import ( + "context" + "fmt" + "math/big" + "strings" + + abci "github.com/cometbft/cometbft/abci/types" + tmtypes "github.com/cometbft/cometbft/types" + + errorsmod "cosmossdk.io/errors" + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + "github.com/cosmos/cosmos-sdk/client" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/NibiruChain/nibiru/v2/x/evm" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethmath "github.com/ethereum/go-ethereum/common/math" + gethcore "github.com/ethereum/go-ethereum/core/types" + gethparams "github.com/ethereum/go-ethereum/params" +) + +// ErrExceedBlockGasLimit defines the error message when tx execution exceeds the +// block gas limit. The tx fee is deducted in ante handler, so it shouldn't be +// ignored in JSON-RPC API. +const ErrExceedBlockGasLimit = "out of gas in location: block gas meter; gasWanted:" + +// ErrStateDBCommit defines the error message when commit after executing EVM +// transaction, for example transfer native token to a distribution module +// account using an evm transaction. Note, the transfer amount cannot be set to +// 0, otherwise this problem will not be triggered. +const ErrStateDBCommit = "failed to commit stateDB" + +// RawTxToEthTx returns a evm MsgEthereum transaction from raw tx bytes. +func RawTxToEthTx(clientCtx client.Context, txBz tmtypes.Tx) ([]*evm.MsgEthereumTx, error) { + tx, err := clientCtx.TxConfig.TxDecoder()(txBz) + if err != nil { + return nil, errorsmod.Wrap(errortypes.ErrJSONUnmarshal, err.Error()) + } + + ethTxs := make([]*evm.MsgEthereumTx, len(tx.GetMsgs())) + for i, msg := range tx.GetMsgs() { + ethTx, ok := msg.(*evm.MsgEthereumTx) + if !ok { + return nil, fmt.Errorf("invalid message type %T, expected %T", msg, &evm.MsgEthereumTx{}) + } + ethTx.Hash = ethTx.AsTransaction().Hash().Hex() + ethTxs[i] = ethTx + } + return ethTxs, nil +} + +// EthHeaderFromTendermint: Converts a Tendermint block header to an Eth header. +func EthHeaderFromTendermint( + header tmtypes.Header, bloom gethcore.Bloom, baseFeeWei *big.Int, +) *gethcore.Header { + txHash := gethcore.EmptyRootHash + if len(header.DataHash) == 0 { + txHash = gethcommon.BytesToHash(header.DataHash) + } + + time := uint64(header.Time.UTC().Unix()) // #nosec G701 + return &gethcore.Header{ + ParentHash: gethcommon.BytesToHash(header.LastBlockID.Hash.Bytes()), + UncleHash: gethcore.EmptyUncleHash, + Coinbase: gethcommon.BytesToAddress(header.ProposerAddress), + Root: gethcommon.BytesToHash(header.AppHash), + TxHash: txHash, + ReceiptHash: gethcore.EmptyRootHash, + Bloom: bloom, + Difficulty: big.NewInt(0), + Number: big.NewInt(header.Height), + GasLimit: 0, + GasUsed: 0, + Time: time, + Extra: []byte{}, + MixDigest: gethcommon.Hash{}, + Nonce: gethcore.BlockNonce{}, + BaseFee: baseFeeWei, + } +} + +// BlockMaxGasFromConsensusParams returns the gas limit for the current block +// from the chain consensus params. +func BlockMaxGasFromConsensusParams( + goCtx context.Context, clientCtx client.Context, blockHeight int64, +) (int64, error) { + tmrpcClient, ok := clientCtx.Client.(tmrpcclient.Client) + if !ok { + panic("incorrect tm rpc client") + } + resConsParams, err := tmrpcClient.ConsensusParams(goCtx, &blockHeight) + defaultGasLimit := int64(^uint32(0)) // #nosec G701 + if err != nil { + return defaultGasLimit, err + } + + gasLimit := resConsParams.ConsensusParams.Block.MaxGas + if gasLimit == -1 { + // Sets gas limit to max uint32 to not error with javascript dev tooling + // This -1 value indicating no block gas limit is set to max uint64 with geth hexutils + // which errors certain javascript dev tooling which only supports up to 53 bits + gasLimit = defaultGasLimit + } + + return gasLimit, nil +} + +// FormatBlock creates an ethereum block from a tendermint header and ethereum-formatted +// transactions. +func FormatBlock( + header tmtypes.Header, size int, gasLimit int64, + gasUsed *big.Int, transactions []any, bloom gethcore.Bloom, + validatorAddr gethcommon.Address, baseFee *big.Int, +) map[string]any { + var transactionsRoot gethcommon.Hash + if len(transactions) == 0 { + transactionsRoot = gethcore.EmptyRootHash + } else { + transactionsRoot = gethcommon.BytesToHash(header.DataHash) + } + + result := map[string]any{ + "number": hexutil.Uint64(header.Height), + "hash": hexutil.Bytes(header.Hash()), + "parentHash": gethcommon.BytesToHash(header.LastBlockID.Hash.Bytes()), + "nonce": gethcore.BlockNonce{}, // PoW specific + "sha3Uncles": gethcore.EmptyUncleHash, // No uncles in Tendermint + "logsBloom": bloom, + "stateRoot": hexutil.Bytes(header.AppHash), + "miner": validatorAddr, + "mixHash": gethcommon.Hash{}, + "difficulty": (*hexutil.Big)(big.NewInt(0)), + "extraData": "0x", + "size": hexutil.Uint64(size), + "gasLimit": hexutil.Uint64(gasLimit), // Static gas limit + "gasUsed": (*hexutil.Big)(gasUsed), + "timestamp": hexutil.Uint64(header.Time.Unix()), + "transactionsRoot": transactionsRoot, + "receiptsRoot": gethcore.EmptyRootHash, + + "uncles": []gethcommon.Hash{}, + "transactions": transactions, + "totalDifficulty": (*hexutil.Big)(big.NewInt(0)), + } + + if baseFee != nil { + result["baseFeePerGas"] = (*hexutil.Big)(baseFee) + } + + return result +} + +// NewRPCTxFromMsg returns a transaction that will serialize to the RPC +// representation, with the given location metadata set (if available). +func NewRPCTxFromMsg( + msg *evm.MsgEthereumTx, + blockHash gethcommon.Hash, + blockNumber, index uint64, + baseFeeWei *big.Int, + chainID *big.Int, +) (*EthTxJsonRPC, error) { + tx := msg.AsTransaction() + return NewRPCTxFromEthTx(tx, blockHash, blockNumber, index, baseFeeWei, chainID) +} + +// NewRPCTxFromEthTx returns a transaction that will serialize to the RPC +// representation, with the given location metadata set (if available). +func NewRPCTxFromEthTx( + tx *gethcore.Transaction, + blockHash gethcommon.Hash, + blockNumber, + index uint64, + baseFee *big.Int, + chainID *big.Int, +) (*EthTxJsonRPC, error) { + // Determine the signer. For replay-protected transactions, use the most + // permissive signer, because we assume that signers are backwards-compatible + // with old transactions. For non-protected transactions, the homestead + // signer is used because the return value of ChainId is zero for unprotected + // transactions. + var signer gethcore.Signer + if tx.Protected() { + signer = gethcore.LatestSignerForChainID(tx.ChainId()) + } else { + signer = gethcore.HomesteadSigner{} + } + from, _ := gethcore.Sender(signer, tx) // #nosec G703 + v, r, s := tx.RawSignatureValues() + result := &EthTxJsonRPC{ + Type: hexutil.Uint64(tx.Type()), + From: from, + Gas: hexutil.Uint64(tx.Gas()), + GasPrice: (*hexutil.Big)(tx.GasPrice()), + Hash: tx.Hash(), + Input: hexutil.Bytes(tx.Data()), + Nonce: hexutil.Uint64(tx.Nonce()), + To: tx.To(), + Value: (*hexutil.Big)(tx.Value()), + V: (*hexutil.Big)(v), + R: (*hexutil.Big)(r), + S: (*hexutil.Big)(s), + ChainID: (*hexutil.Big)(chainID), + } + if blockHash != (gethcommon.Hash{}) { + result.BlockHash = &blockHash + result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) + result.TransactionIndex = (*hexutil.Uint64)(&index) + } + switch tx.Type() { + case gethcore.AccessListTxType: + al := tx.AccessList() + result.Accesses = &al + result.ChainID = (*hexutil.Big)(tx.ChainId()) + case gethcore.DynamicFeeTxType: + al := tx.AccessList() + result.Accesses = &al + result.ChainID = (*hexutil.Big)(tx.ChainId()) + result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) + result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) + // if the transaction has been mined, compute the effective gas price + if baseFee != nil && blockHash != (gethcommon.Hash{}) { + // price = min(tip, gasFeeCap - baseFee) + baseFee + price := gethmath.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) + result.GasPrice = (*hexutil.Big)(price) + } else { + result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) + } + } + return result, nil +} + +// CheckTxFee is an internal function used to check whether the fee of +// the given transaction is _reasonable_(under the cap). +func CheckTxFee(gasPrice *big.Int, gas uint64, cap float64) error { + // Short circuit if there is no cap for transaction fee at all. + if cap == 0 { + return nil + } + totalfee := new(big.Float).SetInt(new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gas))) + oneEther := new(big.Float).SetInt(big.NewInt(gethparams.Ether)) + // quo = rounded(x/y) + feeEth := new(big.Float).Quo(totalfee, oneEther) + // no need to check error from parsing + feeFloat, _ := feeEth.Float64() + if feeFloat > cap { + return fmt.Errorf("tx fee (%.2f ether) exceeds the configured cap (%.2f ether)", feeFloat, cap) + } + return nil +} + +// TxExceedBlockGasLimit returns true if the tx exceeds block gas limit. +func TxExceedBlockGasLimit(res *abci.ResponseDeliverTx) bool { + return strings.Contains(res.Log, ErrExceedBlockGasLimit) +} + +// TxStateDBCommitError returns true if the evm tx commit error. +func TxStateDBCommitError(res *abci.ResponseDeliverTx) bool { + return strings.Contains(res.Log, ErrStateDBCommit) +} + +// TxIsValidEnough returns true if the transaction was successful +// or if it failed with an ExceedBlockGasLimit error or TxStateDBCommitError error +func TxIsValidEnough(res *abci.ResponseDeliverTx) (condition bool, reason string) { + if res.Code == 0 { + return true, "tx succeeded" + } else if TxExceedBlockGasLimit(res) { + return true, "tx exceeded block gas limit" + } else if TxStateDBCommitError(res) { + return true, "tx state db commit error" + } + return false, "unexpected failure" +} diff --git a/eth/rpc/rpc_test.go b/eth/rpc/rpc_test.go new file mode 100644 index 000000000..89321c65d --- /dev/null +++ b/eth/rpc/rpc_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpc_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + cmt "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/client" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +type SuiteRPC struct { + suite.Suite +} + +func TestSuiteRPC(t *testing.T) { + suite.Run(t, new(SuiteRPC)) +} + +func (s *SuiteRPC) TestRawTxToEthTx() { + type TestCase struct { + tx cmt.Tx + clientCtx client.Context + wantErr string + } + type TestCaseSetupFn = func() TestCase + + for _, tcSetup := range []TestCaseSetupFn{ + func() TestCase { + _, _, clientCtx := evmtest.NewEthTxMsgAsCmt(s.T()) + txBz := []byte("tx") + return TestCase{ + tx: txBz, // invalid bytes + clientCtx: clientCtx, // valid clientCtx + wantErr: "failed to unmarshal JSON", + } + }, + func() TestCase { + txBz, _, clientCtx := evmtest.NewEthTxMsgAsCmt(s.T()) + return TestCase{ + tx: txBz, // valid bytes + clientCtx: clientCtx, // valid clientCtx + wantErr: "", // happy + } + }, + } { + tc := tcSetup() + ethTxs, err := rpc.RawTxToEthTx(tc.clientCtx, tc.tx) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr, "ethTxs: %s", ethTxs) + continue + } + s.Require().NoError(err, "ethTxs: %s", ethTxs) + } +} + +func (s *SuiteRPC) TestEthHeaderFromTendermint() { + for _, block := range []*cmt.Block{ + // Some happy path test cases for good measure + cmt.MakeBlock(1, []cmt.Tx{}, nil, nil), + cmt.MakeBlock(420, []cmt.Tx{}, nil, nil), + } { + ethHeader := rpc.EthHeaderFromTendermint( + block.Header, gethcore.Bloom{}, sdkmath.NewInt(1).BigInt()) + s.NotNil(ethHeader) + s.Equal(block.Header.Height, ethHeader.Number.Int64()) + } +} diff --git a/eth/rpc/rpcapi/apis.go b/eth/rpc/rpcapi/apis.go new file mode 100644 index 000000000..6607f92d2 --- /dev/null +++ b/eth/rpc/rpcapi/apis.go @@ -0,0 +1,136 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" + + "github.com/ethereum/go-ethereum/rpc" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + "github.com/NibiruChain/nibiru/v2/eth/rpc/rpcapi/debugapi" + + rpcclient "github.com/cometbft/cometbft/rpc/jsonrpc/client" +) + +// RPC namespaces and API version +const ( + // Cosmos namespaces + NamespaceCosmos = "cosmos" + + // Ethereum namespaces + NamespaceWeb3 = "web3" + NamespaceEth = "eth" + NamespaceNet = "net" + NamespaceTxPool = "txpool" + NamespaceDebug = "debug" + + apiVersion = "1.0" +) + +// APICreator creates the JSON-RPC API implementations. +type APICreator = func( + ctx *server.Context, + clientCtx client.Context, + tendermintWebsocketClient *rpcclient.WSClient, + allowUnprotectedTxs bool, + indexer eth.EVMTxIndexer, +) []rpc.API + +// apiCreators defines the JSON-RPC API namespaces. +var apiCreators map[string]APICreator + +func init() { + apiCreators = map[string]APICreator{ + NamespaceEth: func(ctx *server.Context, + clientCtx client.Context, + tmWSClient *rpcclient.WSClient, + allowUnprotectedTxs bool, + indexer eth.EVMTxIndexer, + ) []rpc.API { + evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer) + return []rpc.API{ + { + Namespace: NamespaceEth, + Version: apiVersion, + Service: NewImplEthAPI(ctx.Logger, evmBackend), + Public: true, + }, + { + Namespace: NamespaceEth, + Version: apiVersion, + Service: NewImplFiltersAPI(ctx.Logger, clientCtx, tmWSClient, evmBackend), + Public: true, + }, + } + }, + NamespaceWeb3: func(*server.Context, client.Context, *rpcclient.WSClient, bool, eth.EVMTxIndexer) []rpc.API { + return []rpc.API{ + { + Namespace: NamespaceWeb3, + Version: apiVersion, + Service: NewImplWeb3API(), + Public: true, + }, + } + }, + NamespaceNet: func(_ *server.Context, clientCtx client.Context, _ *rpcclient.WSClient, _ bool, _ eth.EVMTxIndexer) []rpc.API { + return []rpc.API{ + { + Namespace: NamespaceNet, + Version: apiVersion, + Service: NewImplNetAPI(clientCtx), + Public: true, + }, + } + }, + NamespaceTxPool: func(ctx *server.Context, _ client.Context, _ *rpcclient.WSClient, _ bool, _ eth.EVMTxIndexer) []rpc.API { + return []rpc.API{ + { + Namespace: NamespaceTxPool, + Version: apiVersion, + Service: NewImplTxPoolAPI(ctx.Logger), + Public: true, + }, + } + }, + NamespaceDebug: func(ctx *server.Context, + clientCtx client.Context, + _ *rpcclient.WSClient, + allowUnprotectedTxs bool, + indexer eth.EVMTxIndexer, + ) []rpc.API { + evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, indexer) + return []rpc.API{ + { + Namespace: NamespaceDebug, + Version: apiVersion, + Service: debugapi.NewImplDebugAPI(ctx, evmBackend), + Public: true, + }, + } + }, + } +} + +// GetRPCAPIs returns the list of all APIs +func GetRPCAPIs(ctx *server.Context, + clientCtx client.Context, + tmWSClient *rpcclient.WSClient, + allowUnprotectedTxs bool, + indexer eth.EVMTxIndexer, + selectedAPIs []string, +) []rpc.API { + var apis []rpc.API + + for _, ns := range selectedAPIs { + if creator, ok := apiCreators[ns]; ok { + apis = append(apis, creator(ctx, clientCtx, tmWSClient, allowUnprotectedTxs, indexer)...) + } else { + ctx.Logger.Error("invalid namespace value", "namespace", ns) + } + } + + return apis +} diff --git a/eth/rpc/rpcapi/debugapi/api.go b/eth/rpc/rpcapi/debugapi/api.go new file mode 100644 index 000000000..7ce3fa345 --- /dev/null +++ b/eth/rpc/rpcapi/debugapi/api.go @@ -0,0 +1,359 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package debugapi + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "runtime" // #nosec G702 + "runtime/debug" + "runtime/pprof" + "sync" + "time" + + "github.com/davecgh/go-spew/spew" + + "github.com/NibiruChain/nibiru/v2/x/evm" + + stderrors "github.com/pkg/errors" + + "github.com/cosmos/cosmos-sdk/server" + + "github.com/cometbft/cometbft/libs/log" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/rlp" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" +) + +// HandlerT keeps track of the cpu profiler and trace execution +type HandlerT struct { + cpuFilename string + cpuFile io.WriteCloser + mu sync.Mutex + traceFilename string + traceFile io.WriteCloser +} + +// DebugAPI is the collection of tracing APIs exposed over the private debugging +// endpoint. +type DebugAPI struct { + ctx *server.Context + logger log.Logger + backend *backend.Backend + handler *HandlerT +} + +// NewImplDebugAPI creates a new API definition for the tracing methods of the +// Ethereum service. +func NewImplDebugAPI( + ctx *server.Context, + backend *backend.Backend, +) *DebugAPI { + return &DebugAPI{ + ctx: ctx, + logger: ctx.Logger.With("module", "debug"), + backend: backend, + handler: new(HandlerT), + } +} + +// TraceTransaction returns the structured logs created during the execution of EVM +// and returns them as a JSON object. +func (a *DebugAPI) TraceTransaction(hash common.Hash, config *evm.TraceConfig) (any, error) { + a.logger.Debug("debug_traceTransaction", "hash", hash) + return a.backend.TraceTransaction(hash, config) +} + +// TraceBlockByNumber returns the structured logs created during the execution of +// EVM and returns them as a JSON object. +func (a *DebugAPI) TraceBlockByNumber(height rpc.BlockNumber, config *evm.TraceConfig) ([]*evm.TxTraceResult, error) { + a.logger.Debug("debug_traceBlockByNumber", "height", height) + if height == 0 { + return nil, errors.New("genesis is not traceable") + } + // Get Tendermint Block + resBlock, err := a.backend.TendermintBlockByNumber(height) + if err != nil { + a.logger.Debug("get block failed", "height", height, "error", err.Error()) + return nil, err + } + + return a.backend.TraceBlock(rpc.BlockNumber(resBlock.Block.Height), config, resBlock) +} + +// TraceBlockByHash returns the structured logs created during the execution of +// EVM and returns them as a JSON object. +func (a *DebugAPI) TraceBlockByHash(hash common.Hash, config *evm.TraceConfig) ([]*evm.TxTraceResult, error) { + a.logger.Debug("debug_traceBlockByHash", "hash", hash) + // Get Tendermint Block + resBlock, err := a.backend.TendermintBlockByHash(hash) + if err != nil { + a.logger.Debug("get block failed", "hash", hash.Hex(), "error", err.Error()) + return nil, err + } + + if resBlock == nil || resBlock.Block == nil { + a.logger.Debug("block not found", "hash", hash.Hex()) + return nil, errors.New("block not found") + } + + return a.backend.TraceBlock(rpc.BlockNumber(resBlock.Block.Height), config, resBlock) +} + +// TraceCall implements eth debug_traceCall method which lets you run an eth_call +// within the context of the given block execution using the final state of parent block as the base. +// Method returns the structured logs created during the execution of EVM. +// The method returns the same output as debug_traceTransaction. +// https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugtracecall +func (a *DebugAPI) TraceCall( + args evm.JsonTxArgs, + blockNrOrHash rpc.BlockNumberOrHash, + config *evm.TraceConfig, +) (any, error) { + a.logger.Debug("debug_traceCall", args.String(), "block number or hash", blockNrOrHash) + + // Get Tendermint Block + resBlock, err := a.backend.BlockNumberFromTendermint(blockNrOrHash) + if err != nil { + a.logger.Debug("get block failed", "blockNrOrHash", blockNrOrHash, "error", err.Error()) + return nil, err + } + return a.backend.TraceCall(args, resBlock, config) +} + +// BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to +// file. It uses a profile rate of 1 for most accurate information. If a different rate is +// desired, set the rate and write the profile manually. +func (a *DebugAPI) BlockProfile(file string, nsec uint) error { + a.logger.Debug("debug_blockProfile", "file", file, "nsec", nsec) + runtime.SetBlockProfileRate(1) + defer runtime.SetBlockProfileRate(0) + + time.Sleep(time.Duration(nsec) * time.Second) + return writeProfile("block", file, a.logger) +} + +// CpuProfile turns on CPU profiling for nsec seconds and writes +// profile data to file. +func (a *DebugAPI) CpuProfile(file string, nsec uint) error { //nolint: golint, stylecheck, revive + a.logger.Debug("debug_cpuProfile", "file", file, "nsec", nsec) + if err := a.StartCPUProfile(file); err != nil { + return err + } + time.Sleep(time.Duration(nsec) * time.Second) + return a.StopCPUProfile() +} + +// GcStats returns GC statistics. +func (a *DebugAPI) GcStats() *debug.GCStats { + a.logger.Debug("debug_gcStats") + s := new(debug.GCStats) + debug.ReadGCStats(s) + return s +} + +// GoTrace turns on tracing for nsec seconds and writes +// trace data to file. +func (a *DebugAPI) GoTrace(file string, nsec uint) error { + a.logger.Debug("debug_goTrace", "file", file, "nsec", nsec) + if err := a.StartGoTrace(file); err != nil { + return err + } + time.Sleep(time.Duration(nsec) * time.Second) + return a.StopGoTrace() +} + +// MemStats returns detailed runtime memory statistics. +func (a *DebugAPI) MemStats() *runtime.MemStats { + a.logger.Debug("debug_memStats") + s := new(runtime.MemStats) + runtime.ReadMemStats(s) + return s +} + +// SetBlockProfileRate sets the rate of goroutine block profile data collection. +// rate 0 disables block profiling. +func (a *DebugAPI) SetBlockProfileRate(rate int) { + a.logger.Debug("debug_setBlockProfileRate", "rate", rate) + runtime.SetBlockProfileRate(rate) +} + +// Stacks returns a printed representation of the stacks of all goroutines. +func (a *DebugAPI) Stacks() string { + a.logger.Debug("debug_stacks") + buf := new(bytes.Buffer) + err := pprof.Lookup("goroutine").WriteTo(buf, 2) + if err != nil { + a.logger.Error("Failed to create stacks", "error", err.Error()) + } + return buf.String() +} + +// StartCPUProfile turns on CPU profiling, writing to the given file. +func (a *DebugAPI) StartCPUProfile(file string) error { + a.logger.Debug("debug_startCPUProfile", "file", file) + a.handler.mu.Lock() + defer a.handler.mu.Unlock() + + switch { + case isCPUProfileConfigurationActivated(a.ctx): + a.logger.Debug("CPU profiling already in progress using the configuration file") + return errors.New("CPU profiling already in progress using the configuration file") + case a.handler.cpuFile != nil: + a.logger.Debug("CPU profiling already in progress") + return errors.New("CPU profiling already in progress") + default: + fp, err := ExpandHome(file) + if err != nil { + a.logger.Debug("failed to get filepath for the CPU profile file", "error", err.Error()) + return err + } + f, err := os.Create(fp) + if err != nil { + a.logger.Debug("failed to create CPU profile file", "error", err.Error()) + return err + } + if err := pprof.StartCPUProfile(f); err != nil { + a.logger.Debug("cpu profiling already in use", "error", err.Error()) + if err := f.Close(); err != nil { + a.logger.Debug("failed to close cpu profile file") + return stderrors.Wrap(err, "failed to close cpu profile file") + } + return err + } + + a.logger.Info("CPU profiling started", "profile", file) + a.handler.cpuFile = f + a.handler.cpuFilename = file + return nil + } +} + +// StopCPUProfile stops an ongoing CPU profile. +func (a *DebugAPI) StopCPUProfile() error { + a.logger.Debug("debug_stopCPUProfile") + a.handler.mu.Lock() + defer a.handler.mu.Unlock() + + switch { + case isCPUProfileConfigurationActivated(a.ctx): + a.logger.Debug("CPU profiling already in progress using the configuration file") + return errors.New("CPU profiling already in progress using the configuration file") + case a.handler.cpuFile != nil: + a.logger.Info("Done writing CPU profile", "profile", a.handler.cpuFilename) + pprof.StopCPUProfile() + if err := a.handler.cpuFile.Close(); err != nil { + a.logger.Debug("failed to close cpu file") + return stderrors.Wrap(err, "failed to close cpu file") + } + a.handler.cpuFile = nil + a.handler.cpuFilename = "" + return nil + default: + a.logger.Debug("CPU profiling not in progress") + return errors.New("CPU profiling not in progress") + } +} + +// WriteBlockProfile writes a goroutine blocking profile to the given file. +func (a *DebugAPI) WriteBlockProfile(file string) error { + a.logger.Debug("debug_writeBlockProfile", "file", file) + return writeProfile("block", file, a.logger) +} + +// WriteMemProfile writes an allocation profile to the given file. +// Note that the profiling rate cannot be set through the API, +// it must be set on the command line. +func (a *DebugAPI) WriteMemProfile(file string) error { + a.logger.Debug("debug_writeMemProfile", "file", file) + return writeProfile("heap", file, a.logger) +} + +// MutexProfile turns on mutex profiling for nsec seconds and writes profile data to file. +// It uses a profile rate of 1 for most accurate information. If a different rate is +// desired, set the rate and write the profile manually. +func (a *DebugAPI) MutexProfile(file string, nsec uint) error { + a.logger.Debug("debug_mutexProfile", "file", file, "nsec", nsec) + runtime.SetMutexProfileFraction(1) + time.Sleep(time.Duration(nsec) * time.Second) + defer runtime.SetMutexProfileFraction(0) + return writeProfile("mutex", file, a.logger) +} + +// SetMutexProfileFraction sets the rate of mutex profiling. +func (a *DebugAPI) SetMutexProfileFraction(rate int) { + a.logger.Debug("debug_setMutexProfileFraction", "rate", rate) + runtime.SetMutexProfileFraction(rate) +} + +// WriteMutexProfile writes a goroutine blocking profile to the given file. +func (a *DebugAPI) WriteMutexProfile(file string) error { + a.logger.Debug("debug_writeMutexProfile", "file", file) + return writeProfile("mutex", file, a.logger) +} + +// FreeOSMemory forces a garbage collection. +func (a *DebugAPI) FreeOSMemory() { + a.logger.Debug("debug_freeOSMemory") + debug.FreeOSMemory() +} + +// SetGCPercent sets the garbage collection target percentage. It returns the previous +// setting. A negative value disables GC. +func (a *DebugAPI) SetGCPercent(v int) int { + a.logger.Debug("debug_setGCPercent", "percent", v) + return debug.SetGCPercent(v) +} + +// GetHeaderRlp retrieves the RLP encoded for of a single header. +func (a *DebugAPI) GetHeaderRlp(number uint64) (hexutil.Bytes, error) { + header, err := a.backend.HeaderByNumber(rpc.BlockNumber(number)) + if err != nil { + return nil, err + } + + return rlp.EncodeToBytes(header) +} + +// GetBlockRlp retrieves the RLP encoded for of a single block. +func (a *DebugAPI) GetBlockRlp(number uint64) (hexutil.Bytes, error) { + block, err := a.backend.EthBlockByNumber(rpc.BlockNumber(number)) + if err != nil { + return nil, err + } + + return rlp.EncodeToBytes(block) +} + +// PrintBlock retrieves a block and returns its pretty printed form. +func (a *DebugAPI) PrintBlock(number uint64) (string, error) { + block, err := a.backend.EthBlockByNumber(rpc.BlockNumber(number)) + if err != nil { + return "", err + } + + return spew.Sdump(block), nil +} + +// SeedHash retrieves the seed hash of a block. +func (a *DebugAPI) SeedHash(number uint64) (string, error) { + _, err := a.backend.HeaderByNumber(rpc.BlockNumber(number)) + if err != nil { + return "", err + } + + return fmt.Sprintf("0x%x", ethash.SeedHash(number)), nil +} + +// IntermediateRoots executes a block, and returns a list +// of intermediate roots: the stateroot after each transaction. +func (a *DebugAPI) IntermediateRoots(hash common.Hash, _ *evm.TraceConfig) ([]common.Hash, error) { + a.logger.Debug("debug_intermediateRoots", "hash", hash) + return ([]common.Hash)(nil), nil +} diff --git a/eth/rpc/rpcapi/debugapi/trace.go b/eth/rpc/rpcapi/debugapi/trace.go new file mode 100644 index 000000000..8dce68952 --- /dev/null +++ b/eth/rpc/rpcapi/debugapi/trace.go @@ -0,0 +1,84 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build go1.5 +// +build go1.5 + +package debugapi + +import ( + "errors" + "os" + "runtime/trace" + + stderrors "github.com/pkg/errors" +) + +// StartGoTrace turns on tracing, writing to the given file. +func (a *DebugAPI) StartGoTrace(file string) error { + a.logger.Debug("debug_startGoTrace", "file", file) + a.handler.mu.Lock() + defer a.handler.mu.Unlock() + + if a.handler.traceFile != nil { + a.logger.Debug("trace already in progress") + return errors.New("trace already in progress") + } + fp, err := ExpandHome(file) + if err != nil { + a.logger.Debug("failed to get filepath for the CPU profile file", "error", err.Error()) + return err + } + f, err := os.Create(fp) + if err != nil { + a.logger.Debug("failed to create go trace file", "error", err.Error()) + return err + } + if err := trace.Start(f); err != nil { + a.logger.Debug("Go tracing already started", "error", err.Error()) + if err := f.Close(); err != nil { + a.logger.Debug("failed to close trace file") + return stderrors.Wrap(err, "failed to close trace file") + } + + return err + } + a.handler.traceFile = f + a.handler.traceFilename = file + a.logger.Info("Go tracing started", "dump", a.handler.traceFilename) + return nil +} + +// StopGoTrace stops an ongoing trace. +func (a *DebugAPI) StopGoTrace() error { + a.logger.Debug("debug_stopGoTrace") + a.handler.mu.Lock() + defer a.handler.mu.Unlock() + + trace.Stop() + if a.handler.traceFile == nil { + a.logger.Debug("trace not in progress") + return errors.New("trace not in progress") + } + a.logger.Info("Done writing Go trace", "dump", a.handler.traceFilename) + if err := a.handler.traceFile.Close(); err != nil { + a.logger.Debug("failed to close trace file") + return stderrors.Wrap(err, "failed to close trace file") + } + a.handler.traceFile = nil + a.handler.traceFilename = "" + return nil +} diff --git a/eth/rpc/rpcapi/debugapi/trace_fallback.go b/eth/rpc/rpcapi/debugapi/trace_fallback.go new file mode 100644 index 000000000..8f4c6caa2 --- /dev/null +++ b/eth/rpc/rpcapi/debugapi/trace_fallback.go @@ -0,0 +1,36 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build !go1.5 +// +build !go1.5 + +// no-op implementation of tracing methods for Go < 1.5. + +package debug + +import ( + "errors" +) + +func (*API) StartGoTrace(string file) error { + a.logger.Debug("debug_stopGoTrace", "file", file) + return errors.New("tracing is not supported on Go < 1.5") +} + +func (*API) StopGoTrace() error { + a.logger.Debug("debug_stopGoTrace") + return errors.New("tracing is not supported on Go < 1.5") +} diff --git a/eth/rpc/rpcapi/debugapi/utils.go b/eth/rpc/rpcapi/debugapi/utils.go new file mode 100644 index 000000000..95fbad86e --- /dev/null +++ b/eth/rpc/rpcapi/debugapi/utils.go @@ -0,0 +1,61 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package debugapi + +import ( + "os" + "os/user" + "path/filepath" + "runtime/pprof" + "strings" + + "github.com/cometbft/cometbft/libs/log" + "github.com/cosmos/cosmos-sdk/server" +) + +// isCPUProfileConfigurationActivated: Checks if the "cpu-profile" flag was set +func isCPUProfileConfigurationActivated(ctx *server.Context) bool { + // TODO: use same constants as server/start.go + // constant declared in start.go cannot be imported (cyclical dependency) + const flagCPUProfile = "cpu-profile" + if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" { + return true + } + return false +} + +// ExpandHome expands home directory in file paths. +// ~someuser/tmp will not be expanded. +func ExpandHome(p string) (string, error) { + if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { + usr, err := user.Current() + if err != nil { + return p, err + } + home := usr.HomeDir + p = home + p[1:] + } + return filepath.Clean(p), nil +} + +// writeProfile writes the data to a file +func writeProfile(name, file string, log log.Logger) error { + p := pprof.Lookup(name) + log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file) + fp, err := ExpandHome(file) + if err != nil { + return err + } + f, err := os.Create(fp) + if err != nil { + return err + } + + if err := p.WriteTo(f, 0); err != nil { + if err := f.Close(); err != nil { + return err + } + return err + } + + return f.Close() +} diff --git a/eth/rpc/rpcapi/eth_api.go b/eth/rpc/rpcapi/eth_api.go new file mode 100644 index 000000000..3ada30358 --- /dev/null +++ b/eth/rpc/rpcapi/eth_api.go @@ -0,0 +1,493 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "context" + + gethrpc "github.com/ethereum/go-ethereum/rpc" + + "github.com/cometbft/cometbft/libs/log" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// Ethereum API: Allows connection to a full node of the Nibiru blockchain +// network via Nibiru EVM. Developers can interact with on-chain EVM data and +// send different types of transactions to the network by utilizing the endpoints +// provided by the API. The API follows a JSON-RPC standard. If not otherwise +// specified, the interface is derived from the Alchemy Ethereum API: +// https://docs.alchemy.com/alchemy/apis/ethereum +type IEthAPI interface { + // Getting Blocks + // + // Retrieves information from a particular block in the blockchain. + BlockNumber() (hexutil.Uint64, error) + GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bool) (map[string]any, error) + GetBlockByHash(hash common.Hash, fullTx bool) (map[string]any, error) + GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint + GetBlockTransactionCountByNumber(blockNum rpc.BlockNumber) *hexutil.Uint + + // Reading Transactions + // + // Retrieves information on the state data for addresses regardless of whether + // it is a user or a smart contract. + GetTransactionByHash(hash common.Hash) (*rpc.EthTxJsonRPC, error) + GetTransactionCount(address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) + GetTransactionReceipt(hash common.Hash) (*backend.TransactionReceipt, error) + GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpc.EthTxJsonRPC, error) + GetTransactionByBlockNumberAndIndex(blockNum rpc.BlockNumber, idx hexutil.Uint) (*rpc.EthTxJsonRPC, error) + // eth_getBlockReceipts + + // Writing Transactions + // + // Allows developers to both send ETH from one address to another, write data + // on-chain, and interact with smart contracts. + SendRawTransaction(data hexutil.Bytes) (common.Hash, error) + + // Account Information + // + // Returns information regarding an address's stored on-chain data. + Accounts() ([]common.Address, error) + GetBalance( + address common.Address, blockNrOrHash rpc.BlockNumberOrHash, + ) (*hexutil.Big, error) + GetStorageAt( + address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash, + ) (hexutil.Bytes, error) + GetCode( + address common.Address, blockNrOrHash rpc.BlockNumberOrHash, + ) (hexutil.Bytes, error) + GetProof( + address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash, + ) (*rpc.AccountResult, error) + + // EVM/Smart Contract Execution + // + // Allows developers to read data from the blockchain which includes executing + // smart contracts. However, no data is published to the Ethereum network. + Call( + args evm.JsonTxArgs, blockNrOrHash rpc.BlockNumberOrHash, _ *rpc.StateOverride, + ) (hexutil.Bytes, error) + + // Chain Information + // + // Returns information on the Ethereum network and internal settings. + ProtocolVersion() hexutil.Uint + GasPrice() (*hexutil.Big, error) + EstimateGas( + args evm.JsonTxArgs, blockNrOptional *rpc.BlockNumber, + ) (hexutil.Uint64, error) + FeeHistory( + blockCount gethrpc.DecimalOrHex, lastBlock gethrpc.BlockNumber, rewardPercentiles []float64, + ) (*rpc.FeeHistoryResult, error) + MaxPriorityFeePerGas() (*hexutil.Big, error) + ChainId() (*hexutil.Big, error) + + // Getting Uncles + // + // Returns information on uncle blocks are which are network rejected blocks + // and replaced by a canonical block instead. + GetUncleByBlockHashAndIndex( + hash common.Hash, idx hexutil.Uint, + ) map[string]any + GetUncleByBlockNumberAndIndex( + number, idx hexutil.Uint, + ) map[string]any + GetUncleCountByBlockHash(hash common.Hash) hexutil.Uint + GetUncleCountByBlockNumber(blockNum rpc.BlockNumber) hexutil.Uint + + // Other + Syncing() (any, error) + GetTransactionLogs(txHash common.Hash) ([]*gethcore.Log, error) + FillTransaction( + args evm.JsonTxArgs, + ) (*rpc.SignTransactionResult, error) + GetPendingTransactions() ([]*rpc.EthTxJsonRPC, error) +} + +var _ IEthAPI = (*EthAPI)(nil) + +// EthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. +type EthAPI struct { + ctx context.Context + logger log.Logger + backend *backend.Backend +} + +// NewImplEthAPI creates an instance of the public ETH Web3 API. +func NewImplEthAPI(logger log.Logger, backend *backend.Backend) *EthAPI { + api := &EthAPI{ + ctx: context.Background(), + logger: logger.With("client", "json-rpc"), + backend: backend, + } + + return api +} + +// -------------------------------------------------------------------------- +// Blocks +// -------------------------------------------------------------------------- + +// BlockNumber returns the current block number. +func (e *EthAPI) BlockNumber() (hexutil.Uint64, error) { + e.logger.Debug("eth_blockNumber") + return e.backend.BlockNumber() +} + +// GetBlockByNumber returns the block identified by number. +func (e *EthAPI) GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bool) (map[string]any, error) { + e.logger.Debug("eth_getBlockByNumber", "number", ethBlockNum, "full", fullTx) + return e.backend.GetBlockByNumber(ethBlockNum, fullTx) +} + +// GetBlockByHash returns the block identified by hash. +func (e *EthAPI) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]any, error) { + e.logger.Debug("eth_getBlockByHash", "hash", hash.Hex(), "full", fullTx) + return e.backend.GetBlockByHash(hash, fullTx) +} + +// -------------------------------------------------------------------------- +// Read Txs +// -------------------------------------------------------------------------- + +// GetTransactionByHash returns the transaction identified by hash. +func (e *EthAPI) GetTransactionByHash(hash common.Hash) (*rpc.EthTxJsonRPC, error) { + e.logger.Debug("eth_getTransactionByHash", "hash", hash.Hex()) + return e.backend.GetTransactionByHash(hash) +} + +// GetTransactionCount returns the number of transactions at the given address up to the given block number. +func (e *EthAPI) GetTransactionCount( + address common.Address, blockNrOrHash rpc.BlockNumberOrHash, +) (*hexutil.Uint64, error) { + e.logger.Debug("eth_getTransactionCount", "address", address.Hex(), "block number or hash", blockNrOrHash) + blockNum, err := e.backend.BlockNumberFromTendermint(blockNrOrHash) + if err != nil { + return nil, err + } + return e.backend.GetTransactionCount(address, blockNum) +} + +// GetTransactionReceipt returns the transaction receipt identified by hash. +func (e *EthAPI) GetTransactionReceipt( + hash common.Hash, +) (*backend.TransactionReceipt, error) { + hexTx := hash.Hex() + e.logger.Debug("eth_getTransactionReceipt", "hash", hexTx) + return e.backend.GetTransactionReceipt(hash) +} + +// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash. +func (e *EthAPI) GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint { + e.logger.Debug("eth_getBlockTransactionCountByHash", "hash", hash.Hex()) + return e.backend.GetBlockTransactionCountByHash(hash) +} + +// GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number. +func (e *EthAPI) GetBlockTransactionCountByNumber( + blockNum rpc.BlockNumber, +) *hexutil.Uint { + e.logger.Debug("eth_getBlockTransactionCountByNumber", "height", blockNum.Int64()) + return e.backend.GetBlockTransactionCountByNumber(blockNum) +} + +// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index. +func (e *EthAPI) GetTransactionByBlockHashAndIndex( + hash common.Hash, idx hexutil.Uint, +) (*rpc.EthTxJsonRPC, error) { + e.logger.Debug("eth_getTransactionByBlockHashAndIndex", "hash", hash.Hex(), "index", idx) + return e.backend.GetTransactionByBlockHashAndIndex(hash, idx) +} + +// GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index. +func (e *EthAPI) GetTransactionByBlockNumberAndIndex( + blockNum rpc.BlockNumber, idx hexutil.Uint, +) (*rpc.EthTxJsonRPC, error) { + e.logger.Debug("eth_getTransactionByBlockNumberAndIndex", "number", blockNum, "index", idx) + return e.backend.GetTransactionByBlockNumberAndIndex(blockNum, idx) +} + +// -------------------------------------------------------------------------- +// Write Txs +// -------------------------------------------------------------------------- + +// SendRawTransaction send a raw Ethereum transaction. +func (e *EthAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) { + e.logger.Debug("eth_sendRawTransaction", "length", len(data)) + return e.backend.SendRawTransaction(data) +} + +// -------------------------------------------------------------------------- +// Account Information +// -------------------------------------------------------------------------- + +// Accounts returns the list of accounts available to this node. +func (e *EthAPI) Accounts() ([]common.Address, error) { + e.logger.Debug("eth_accounts") + return e.backend.Accounts() +} + +// GetBalance returns the provided account's balance up to the provided block number. +func (e *EthAPI) GetBalance( + address common.Address, blockNrOrHash rpc.BlockNumberOrHash, +) (*hexutil.Big, error) { + e.logger.Debug("eth_getBalance", "address", address.String(), "block number or hash", blockNrOrHash) + return e.backend.GetBalance(address, blockNrOrHash) +} + +// GetStorageAt returns the contract storage at the given address, block number, and key. +func (e *EthAPI) GetStorageAt( + address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash, +) (hexutil.Bytes, error) { + e.logger.Debug("eth_getStorageAt", "address", address.Hex(), "key", key, "block number or hash", blockNrOrHash) + return e.backend.GetStorageAt(address, key, blockNrOrHash) +} + +// GetCode returns the contract code at the given address and block number. +func (e *EthAPI) GetCode( + address common.Address, blockNrOrHash rpc.BlockNumberOrHash, +) (hexutil.Bytes, error) { + e.logger.Debug("eth_getCode", "address", address.Hex(), "block number or hash", blockNrOrHash) + return e.backend.GetCode(address, blockNrOrHash) +} + +// GetProof returns an account object with proof and any storage proofs +func (e *EthAPI) GetProof(address common.Address, + storageKeys []string, + blockNrOrHash rpc.BlockNumberOrHash, +) (*rpc.AccountResult, error) { + e.logger.Debug("eth_getProof", "address", address.Hex(), "keys", storageKeys, "block number or hash", blockNrOrHash) + return e.backend.GetProof(address, storageKeys, blockNrOrHash) +} + +// -------------------------------------------------------------------------- +// EVM/Smart Contract Execution +// -------------------------------------------------------------------------- + +// Call performs a raw contract call. +func (e *EthAPI) Call(args evm.JsonTxArgs, + blockNrOrHash rpc.BlockNumberOrHash, + _ *rpc.StateOverride, +) (hexutil.Bytes, error) { + e.logger.Debug("eth_call", "args", args.String(), "block number or hash", blockNrOrHash) + + blockNum, err := e.backend.BlockNumberFromTendermint(blockNrOrHash) + if err != nil { + return nil, err + } + data, err := e.backend.DoCall(args, blockNum) + if err != nil { + return []byte{}, err + } + + return (hexutil.Bytes)(data.Ret), nil +} + +// -------------------------------------------------------------------------- +// Event Logs +// -------------------------------------------------------------------------- +// FILTER API at ./filters/api.go + +// -------------------------------------------------------------------------- +// Chain Information +// -------------------------------------------------------------------------- + +// ProtocolVersion returns the supported Ethereum protocol version. +func (e *EthAPI) ProtocolVersion() hexutil.Uint { + e.logger.Debug("eth_protocolVersion") + return hexutil.Uint(eth.ProtocolVersion) +} + +// GasPrice returns the current gas price based on Ethermint's gas price oracle. +func (e *EthAPI) GasPrice() (*hexutil.Big, error) { + e.logger.Debug("eth_gasPrice") + return e.backend.GasPrice() +} + +// EstimateGas returns an estimate of gas usage for the given smart contract call. +func (e *EthAPI) EstimateGas( + args evm.JsonTxArgs, blockNrOptional *rpc.BlockNumber, +) (hexutil.Uint64, error) { + e.logger.Debug("eth_estimateGas") + return e.backend.EstimateGas(args, blockNrOptional) +} + +func (e *EthAPI) FeeHistory(blockCount gethrpc.DecimalOrHex, + lastBlock gethrpc.BlockNumber, + rewardPercentiles []float64, +) (*rpc.FeeHistoryResult, error) { + e.logger.Debug("eth_feeHistory") + return e.backend.FeeHistory(blockCount, lastBlock, rewardPercentiles) +} + +// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic fee +// transactions. +func (e *EthAPI) MaxPriorityFeePerGas() (*hexutil.Big, error) { + e.logger.Debug("eth_maxPriorityFeePerGas") + head, err := e.backend.CurrentHeader() + if err != nil { + return nil, err + } + tipcap, err := e.backend.SuggestGasTipCap(head.BaseFee) + if err != nil { + return nil, err + } + return (*hexutil.Big)(tipcap), nil +} + +// ChainId is the EIP-155 replay-protection chain id for the current ethereum +// chain config. +func (e *EthAPI) ChainId() (*hexutil.Big, error) { //nolint + e.logger.Debug("eth_chainId") + return e.backend.ChainID(), nil +} + +// -------------------------------------------------------------------------- +// Uncles +// -------------------------------------------------------------------------- + +// GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. +// Always returns nil. +func (e *EthAPI) GetUncleByBlockHashAndIndex( + _ common.Hash, _ hexutil.Uint, +) map[string]any { + return nil +} + +// GetUncleByBlockNumberAndIndex returns the uncle identified by number and +// index. Always returns nil. +func (e *EthAPI) GetUncleByBlockNumberAndIndex( + _, _ hexutil.Uint, +) map[string]any { + return nil +} + +// GetUncleCountByBlockHash returns the number of uncles in the block identified +// by hash. Always zero. +func (e *EthAPI) GetUncleCountByBlockHash(_ common.Hash) hexutil.Uint { + return 0 +} + +// GetUncleCountByBlockNumber returns the number of uncles in the block +// identified by number. Always zero. +func (e *EthAPI) GetUncleCountByBlockNumber(_ rpc.BlockNumber) hexutil.Uint { + return 0 +} + +// -------------------------------------------------------------------------- +// Other +// -------------------------------------------------------------------------- + +// Syncing returns false in case the node is currently not syncing with the +// network. It can be up to date or has not yet received the latest block headers +// from its pears. In case it is synchronizing: +// +// - startingBlock: block number this node started to synchronize from +// - currentBlock: block number this node is currently importing +// - highestBlock: block number of the highest block header this node has received from peers +// - pulledStates: number of state entries processed until now +// - knownStates: number of known state entries that still need to be pulled +func (e *EthAPI) Syncing() (any, error) { + e.logger.Debug("eth_syncing") + return e.backend.Syncing() +} + +// GetTransactionLogs returns the logs given a transaction hash. +func (e *EthAPI) GetTransactionLogs(txHash common.Hash) ([]*gethcore.Log, error) { + e.logger.Debug("eth_getTransactionLogs", "hash", txHash) + + hexTx := txHash.Hex() + res, err := e.backend.GetTxByEthHash(txHash) + if err != nil { + e.logger.Debug("tx not found", "hash", hexTx, "error", err.Error()) + return nil, nil + } + + if res.Failed { + // failed, return empty logs + return nil, nil + } + + resBlockResult, err := e.backend.TendermintBlockResultByNumber(&res.Height) + if err != nil { + e.logger.Debug("block result not found", "number", res.Height, "error", err.Error()) + return nil, nil + } + + // parse tx logs from events + index := int(res.MsgIndex) // #nosec G701 + return backend.TxLogsFromEvents(resBlockResult.TxsResults[res.TxIndex].Events, index) +} + +// FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields) +// on a given unsigned transaction, and returns it to the caller for further +// processing (signing + broadcast). +func (e *EthAPI) FillTransaction( + args evm.JsonTxArgs, +) (*rpc.SignTransactionResult, error) { + // Set some sanity defaults and terminate on failure + args, err := e.backend.SetTxDefaults(args) + if err != nil { + return nil, err + } + + // Assemble the transaction and obtain rlp + tx := args.ToMsgEthTx().AsTransaction() + + data, err := tx.MarshalBinary() + if err != nil { + return nil, err + } + + return &rpc.SignTransactionResult{ + Raw: data, + Tx: tx, + }, nil +} + +// GetPendingTransactions returns the transactions that are in the transaction +// pool and have a from address that is one of the accounts this node manages. +func (e *EthAPI) GetPendingTransactions() ([]*rpc.EthTxJsonRPC, error) { + e.logger.Debug("eth_getPendingTransactions") + + txs, err := e.backend.PendingTransactions() + if err != nil { + return nil, err + } + + result := make([]*rpc.EthTxJsonRPC, 0, len(txs)) + for _, tx := range txs { + for _, msg := range (*tx).GetMsgs() { + ethMsg, ok := msg.(*evm.MsgEthereumTx) + if !ok { + // not valid ethereum tx + break + } + + rpctx, err := rpc.NewRPCTxFromMsg( + ethMsg, + common.Hash{}, + uint64(0), + uint64(0), + nil, + e.backend.ChainConfig().ChainID, + ) + if err != nil { + return nil, err + } + + result = append(result, rpctx) + } + } + + return result, nil +} diff --git a/eth/rpc/rpcapi/eth_api_test.go b/eth/rpc/rpcapi/eth_api_test.go new file mode 100644 index 000000000..cb42021ef --- /dev/null +++ b/eth/rpc/rpcapi/eth_api_test.go @@ -0,0 +1,449 @@ +package rpcapi_test + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "strings" + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + geth "github.com/ethereum/go-ethereum" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc/rpcapi" + "github.com/NibiruChain/nibiru/v2/gosdk" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" +) + +var ( + _ suite.TearDownAllSuite = (*NodeSuite)(nil) + _ suite.SetupAllSuite = (*NodeSuite)(nil) +) + +type NodeSuite struct { + suite.Suite + cfg testnetwork.Config + network *testnetwork.Network + val *testnetwork.Validator + + ethClient *ethclient.Client + ethAPI *rpcapi.EthAPI + + fundedAccPrivateKey *ecdsa.PrivateKey + fundedAccEthAddr gethcommon.Address + fundedAccNibiAddr sdk.AccAddress + + contractData embeds.CompiledEvmContract +} + +func TestSuite_RunAll(t *testing.T) { + suite.Run(t, new(Suite)) + + testutil.RetrySuiteRunIfDbClosed(t, func() { + suite.Run(t, new(NodeSuite)) + }, 2) +} + +// SetupSuite runs before every test in the suite. Implements the +// "suite.SetupAllSuite" interface. +func (s *NodeSuite) SetupSuite() { + testutil.BeforeIntegrationSuite(s.T()) + testapp.EnsureNibiruPrefix() + + genState := genesis.NewTestGenesisState(app.MakeEncodingConfig()) + homeDir := s.T().TempDir() + s.cfg = testnetwork.BuildNetworkConfig(genState) + network, err := testnetwork.New(s.T(), homeDir, s.cfg) + s.Require().NoError(err) + + s.network = network + s.val = network.Validators[0] + s.ethClient = s.val.JSONRPCClient + s.ethAPI = s.val.EthRPC_ETH + s.contractData = embeds.SmartContract_TestERC20 + + testAccPrivateKey, _ := crypto.GenerateKey() + s.fundedAccPrivateKey = testAccPrivateKey + s.fundedAccEthAddr = crypto.PubkeyToAddress(testAccPrivateKey.PublicKey) + s.fundedAccNibiAddr = eth.EthAddrToNibiruAddr(s.fundedAccEthAddr) + + funds := sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 100_000_000)) // 10 NIBI + _, err = testnetwork.FillWalletFromValidator( + s.fundedAccNibiAddr, funds, s.val, eth.EthBaseDenom, + ) + s.Require().NoError(err) + s.NoError(s.network.WaitForNextBlock()) +} + +// Test_ChainID EVM method: eth_chainId +func (s *NodeSuite) Test_ChainID() { + ethChainID, err := s.ethClient.ChainID(context.Background()) + s.NoError(err) + s.Equal(appconst.ETH_CHAIN_ID_DEFAULT, ethChainID.Int64()) +} + +// Test_BlockNumber EVM method: eth_blockNumber +func (s *NodeSuite) Test_BlockNumber() { + networkBlockNumber, err := s.network.LatestHeight() + s.NoError(err) + + ethBlockNumber, err := s.ethClient.BlockNumber(context.Background()) + s.NoError(err) + // It might be off by 1 block in either direction. + blockDiff := networkBlockNumber - int64(ethBlockNumber) + s.Truef(blockDiff <= 2, "networkBlockNumber %d, ethBlockNumber %d", + networkBlockNumber, ethBlockNumber, + ) +} + +// Test_BlockByNumber EVM method: eth_getBlockByNumber +func (s *NodeSuite) Test_BlockByNumber() { + networkBlockNumber, err := s.network.LatestHeight() + s.NoError(err) + + ethBlock, err := s.ethClient.BlockByNumber(context.Background(), big.NewInt(networkBlockNumber)) + s.NoError(err) + s.NoError(ethBlock.SanityCheck()) +} + +// Test_BalanceAt EVM method: eth_getBalance +func (s *NodeSuite) Test_BalanceAt() { + testAccEthAddr := gethcommon.BytesToAddress(testnetwork.NewAccount(s.network, "new-user")) + + // New user balance should be 0 + balance, err := s.ethClient.BalanceAt(context.Background(), testAccEthAddr, nil) + s.NoError(err) + s.NotNil(balance) + s.Equal(int64(0), balance.Int64()) + + // Funded account balance should be > 0 + balance, err = s.ethClient.BalanceAt(context.Background(), s.fundedAccEthAddr, nil) + s.NoError(err) + s.NotNil(balance) + s.Greater(balance.Int64(), int64(0)) +} + +// Test_StorageAt EVM method: eth_getStorageAt +func (s *NodeSuite) Test_StorageAt() { + storage, err := s.ethClient.StorageAt( + context.Background(), s.fundedAccEthAddr, gethcommon.Hash{}, nil, + ) + s.NoError(err) + // TODO: add more checks + s.NotNil(storage) +} + +// Test_PendingStorageAt EVM method: eth_getStorageAt | pending +func (s *NodeSuite) Test_PendingStorageAt() { + storage, err := s.ethClient.PendingStorageAt( + context.Background(), s.fundedAccEthAddr, gethcommon.Hash{}, + ) + s.NoError(err) + + // TODO: add more checks + s.NotNil(storage) +} + +// Test_CodeAt EVM method: eth_getCode +func (s *NodeSuite) Test_CodeAt() { + code, err := s.ethClient.CodeAt(context.Background(), s.fundedAccEthAddr, nil) + s.NoError(err) + + // TODO: add more checks + s.NotNil(code) +} + +// Test_PendingCodeAt EVM method: eth_getCode +func (s *NodeSuite) Test_PendingCodeAt() { + code, err := s.ethClient.PendingCodeAt(context.Background(), s.fundedAccEthAddr) + s.NoError(err) + s.NotNil(code) +} + +// Test_EstimateGas EVM method: eth_estimateGas +func (s *NodeSuite) Test_EstimateGas() { + testAccEthAddr := gethcommon.BytesToAddress(testnetwork.NewAccount(s.network, "new-user")) + gasLimit := uint64(21000) + msg := geth.CallMsg{ + From: s.fundedAccEthAddr, + To: &testAccEthAddr, + Gas: gasLimit, + Value: evm.NativeToWei(big.NewInt(1)), + } + gasEstimated, err := s.ethClient.EstimateGas(context.Background(), msg) + s.NoError(err) + s.Equal(fmt.Sprintf("%d", gasLimit), fmt.Sprintf("%d", gasEstimated)) + + for _, msgValue := range []*big.Int{ + big.NewInt(1), + new(big.Int).Sub(evm.NativeToWei(big.NewInt(1)), big.NewInt(1)), // 10^12 - 1 + } { + msg.Value = msgValue + _, err = s.ethClient.EstimateGas(context.Background(), msg) + s.ErrorContains(err, "wei amount is too small") + } +} + +// Test_SuggestGasPrice EVM method: eth_gasPrice +func (s *NodeSuite) Test_SuggestGasPrice() { + // TODO: the backend method is stubbed to 0 + _, err := s.ethClient.SuggestGasPrice(context.Background()) + s.NoError(err) +} + +// Test_SimpleTransferTransaction EVM method: eth_sendRawTransaction +func (s *NodeSuite) Test_SimpleTransferTransaction() { + chainID, err := s.ethClient.ChainID(context.Background()) + s.NoError(err) + nonce, err := s.ethClient.PendingNonceAt(context.Background(), s.fundedAccEthAddr) + s.NoError(err) + + recipientAddr := gethcommon.BytesToAddress(testnetwork.NewAccount(s.network, "recipient")) + recipientBalanceBefore, err := s.ethClient.BalanceAt(context.Background(), recipientAddr, nil) + s.Require().NoError(err) + s.Equal(int64(0), recipientBalanceBefore.Int64()) + + s.T().Log("make sure the sender has enough funds") + weiToSend := evm.NativeToWei(big.NewInt(1)) // 1 unibi + funds := sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 5_000_000)) // 5 * 10^6 unibi + _, err = testnetwork.FillWalletFromValidator( + s.fundedAccNibiAddr, funds, s.network.Validators[0], eth.EthBaseDenom, + ) + s.Require().NoError(err) + s.NoError(s.network.WaitForNextBlock()) + + senderBalanceBeforeWei, err := s.ethClient.BalanceAt( + context.Background(), s.fundedAccEthAddr, nil, + ) + s.NoError(err) + + grpcUrl := s.network.Validators[0].AppConfig.GRPC.Address + grpcConn, err := gosdk.GetGRPCConnection(grpcUrl, true, 5) + s.NoError(err) + + { + querier := bank.NewQueryClient(grpcConn) + resp, err := querier.Balance(context.Background(), &bank.QueryBalanceRequest{ + Address: s.fundedAccNibiAddr.String(), + Denom: eth.EthBaseDenom, + }) + s.Require().NoError(err) + s.Equal("105"+strings.Repeat("0", 6), resp.Balance.Amount.String()) + } + + s.T().Logf("Sending %d wei to %s", weiToSend, recipientAddr.Hex()) + signer := gethcore.LatestSignerForChainID(chainID) + gasPrice := evm.NativeToWei(big.NewInt(1)) + tx, err := gethcore.SignNewTx( + s.fundedAccPrivateKey, + signer, + &gethcore.LegacyTx{ + Nonce: nonce, + To: &recipientAddr, + Value: weiToSend, + Gas: params.TxGas, + GasPrice: gasPrice, // 1 micronibi per gas + }) + s.NoError(err) + err = s.ethClient.SendTransaction(context.Background(), tx) + s.Require().NoError(err) + s.NoError(s.network.WaitForNextBlock()) + + s.NoError(s.network.WaitForNextBlock()) + s.NoError(s.network.WaitForNextBlock()) + + txReceipt, err := s.ethClient.TransactionReceipt(blankCtx, tx.Hash()) + s.NoError(err) + + s.T().Log("Assert event expectations - successful eth tx") + { + blockHeightOfTx := int64(txReceipt.BlockNumber.Uint64()) + blockOfTx, err := s.val.RPCClient.BlockResults(blankCtx, &blockHeightOfTx) + s.NoError(err) + events := blockOfTx.TxsResults[0].Events + pendingEthTxEventHash := gethcommon.Hash{} + pendingEthTxEventIndex := int32(-1) + for _, event := range events { + if event.Type == evm.PendingEthereumTxEvent { + pendingEthTxEventHash, pendingEthTxEventIndex, err = evm.GetEthHashAndIndexFromPendingEthereumTxEvent(event) + s.Require().NoError(err) + } + if event.Type == evm.TypeUrlEventEthereumTx { + ethereumTx, err := evm.EventEthereumTxFromABCIEvent(event) + s.Require().NoError(err) + s.Require().Equal( + pendingEthTxEventHash.Hex(), + ethereumTx.EthHash, + "hash of pending ethereum tx event and ethereum tx event must be equal", + ) + s.Require().Equal( + fmt.Sprintf("%d", pendingEthTxEventIndex), + ethereumTx.Index, + "index of pending ethereum tx event and ethereum tx event must be equal", + ) + } + } + } + + s.T().Log("Assert balances") + senderBalanceAfterWei, err := s.ethClient.BalanceAt(context.Background(), s.fundedAccEthAddr, nil) + s.NoError(err) + + costOfTx := new(big.Int).Add( + weiToSend, + new(big.Int).Mul((new(big.Int).SetUint64(params.TxGas)), gasPrice), + ) + wantSenderBalWei := new(big.Int).Sub(senderBalanceBeforeWei, costOfTx) + s.Equal(wantSenderBalWei.String(), senderBalanceAfterWei.String(), "surpising sender balance") + + recipientBalanceAfter, err := s.ethClient.BalanceAt(context.Background(), recipientAddr, nil) + s.NoError(err) + s.Equal(weiToSend.String(), recipientBalanceAfter.String()) +} + +var blankCtx = context.Background() + +// Test_SmartContract includes contract deployment, query, execution +func (s *NodeSuite) Test_SmartContract() { + chainID, err := s.ethClient.ChainID(context.Background()) + s.NoError(err) + nonce, err := s.ethClient.NonceAt(context.Background(), s.fundedAccEthAddr, nil) + s.NoError(err) + + s.T().Log("Make sure the account has funds.") + + funds := sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 1_000_000_000)) + _, err = testnetwork.FillWalletFromValidator( + s.fundedAccNibiAddr, funds, s.network.Validators[0], eth.EthBaseDenom, + ) + s.Require().NoError(err) + s.NoError(s.network.WaitForNextBlock()) + + grpcUrl := s.network.Validators[0].AppConfig.GRPC.Address + grpcConn, err := gosdk.GetGRPCConnection(grpcUrl, true, 5) + s.NoError(err) + + querier := bank.NewQueryClient(grpcConn) + resp, err := querier.Balance(context.Background(), &bank.QueryBalanceRequest{ + Address: s.fundedAccNibiAddr.String(), + Denom: eth.EthBaseDenom, + }) + s.Require().NoError(err) + // Expect 1.005 billion because of the setup function before this test. + s.True(resp.Balance.Amount.GT(math.NewInt(1_004_900_000)), "unexpectedly low balance ", resp.Balance.Amount.String()) + + s.T().Log("Deploy contract") + signer := gethcore.LatestSignerForChainID(chainID) + txData := s.contractData.Bytecode + tx, err := gethcore.SignNewTx( + s.fundedAccPrivateKey, + signer, + &gethcore.LegacyTx{ + Nonce: nonce, + Gas: 100_500_000 + params.TxGasContractCreation, + GasPrice: evm.NativeToWei(new(big.Int).Add( + evm.BASE_FEE_MICRONIBI, big.NewInt(0), + )), + Data: txData, + }) + s.Require().NoError(err) + + txBz, err := tx.MarshalBinary() + s.NoError(err) + txHash, err := s.ethAPI.SendRawTransaction(txBz) + s.Require().NoError(err) + + s.T().Log("Wait a few blocks so the tx won't be pending") + for i := 0; i < 5; i++ { + _ = s.network.WaitForNextBlock() + } + + s.T().Log("Assert: tx NOT pending") + { + wantCount := 0 + pending, err := s.ethClient.PendingTransactionCount(blankCtx) + s.NoError(err) + s.Require().EqualValues(uint(wantCount), pending) + + pendingTxs, err := s.ethAPI.GetPendingTransactions() + s.NoError(err) + s.Require().Len(pendingTxs, wantCount) + + // This query will succeed only if a receipt is found + _, err = s.ethClient.TransactionReceipt(blankCtx, txHash) + s.Require().NoErrorf(err, "receipt for txHash: %s", txHash.Hex()) + + // This query succeeds if no receipt is found + _, err = s.ethAPI.GetTransactionReceipt(txHash) + s.Require().NoError(err) + } + + { + weiToSend := evm.NativeToWei(big.NewInt(1)) // 1 unibi + s.T().Logf("Sending %d wei (sanity check)", weiToSend) + accResp, err := s.val.EthRpcQueryClient.QueryClient.EthAccount(blankCtx, + &evm.QueryEthAccountRequest{ + Address: s.fundedAccEthAddr.Hex(), + }) + s.NoError(err) + nonce := accResp.Nonce + recipientAddr := gethcommon.BytesToAddress(testnetwork.NewAccount(s.network, "recipient")) + + signer := gethcore.LatestSignerForChainID(chainID) + gasPrice := evm.NativeToWei(big.NewInt(1)) + tx, err := gethcore.SignNewTx( + s.fundedAccPrivateKey, + signer, + &gethcore.LegacyTx{ + Nonce: nonce, + To: &recipientAddr, + Value: weiToSend, + Gas: params.TxGas, + GasPrice: gasPrice, // 1 micronibi per gas + }) + s.Require().NoError(err) + txBz, err := tx.MarshalBinary() + s.NoError(err) + txHash, err := s.ethAPI.SendRawTransaction(txBz) + s.Require().NoError(err) + _ = s.network.WaitForNextBlock() + + txReceipt, err := s.ethClient.TransactionReceipt(blankCtx, txHash) + s.Require().NoError(err) + s.NotNil(txReceipt) + + txHashFromReceipt := txReceipt.TxHash + s.Equal(txHash, txHashFromReceipt) + + // TODO: Test eth_getTransactionByHash using a JSON-RPC request at the + // endpoint directly. + tx, _, err = s.ethClient.TransactionByHash(blankCtx, txHash) + s.NoError(err) + s.NotNil(tx) + } +} + +func (s *NodeSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} diff --git a/eth/rpc/rpcapi/eth_filters_api.go b/eth/rpc/rpcapi/eth_filters_api.go new file mode 100644 index 000000000..eca8dd27c --- /dev/null +++ b/eth/rpc/rpcapi/eth_filters_api.go @@ -0,0 +1,654 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "context" + "fmt" + "math/big" + "sync" + "time" + + "github.com/cosmos/cosmos-sdk/client" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + rpcbackend "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + + "github.com/cometbft/cometbft/libs/log" + + coretypes "github.com/cometbft/cometbft/rpc/core/types" + rpcclient "github.com/cometbft/cometbft/rpc/jsonrpc/client" + tmtypes "github.com/cometbft/cometbft/types" + + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" + gethrpc "github.com/ethereum/go-ethereum/rpc" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// FiltersAPI offers support to create and manage filters. This will allow +// external clients to retrieve various information related to the Ethereum +// protocol such as blocks, transactions and logs. +type FiltersAPI struct { + logger log.Logger + clientCtx client.Context + backend *rpcbackend.Backend + events *EventSubscriber + filtersMu sync.Mutex + filters map[gethrpc.ID]*filter +} + +// consider a filter inactive if it has not been polled for within deadlineForInactivity +func deadlineForInactivity() time.Duration { return 5 * time.Minute } + +// filter is a helper struct that holds meta information over the filter type and +// associated subscription in the event system. +type filter struct { + typ filters.Type + deadline *time.Timer // filter is inactive when deadline triggers + hashes []common.Hash + crit filters.FilterCriteria + logs []*gethcore.Log + s *Subscription // associated subscription in event system +} + +// NewImplFiltersAPI returns a new FiltersAPI instance. +func NewImplFiltersAPI( + logger log.Logger, + clientCtx client.Context, + tmWSClient *rpcclient.WSClient, + backend *rpcbackend.Backend, +) *FiltersAPI { + logger = logger.With("api", "filter") + api := &FiltersAPI{ + logger: logger, + clientCtx: clientCtx, + backend: backend, + filters: make(map[gethrpc.ID]*filter), + events: NewEventSubscriber(logger, tmWSClient), + } + + go api.timeoutLoop() + + return api +} + +// timeoutLoop runs every 5 minutes and deletes filters that have not been recently used. +// Tt is started when the api is created. +func (api *FiltersAPI) timeoutLoop() { + ticker := time.NewTicker(deadlineForInactivity()) + defer ticker.Stop() + + for { + <-ticker.C + api.filtersMu.Lock() + // #nosec G705 + for id, f := range api.filters { + select { + case <-f.deadline.C: + f.s.Unsubscribe(api.events) + delete(api.filters, id) + default: + continue + } + } + api.filtersMu.Unlock() + } +} + +// NewPendingTransactionFilter creates a filter that fetches pending transaction +// hashes as transactions enter the pending state. +// +// It is part of the filter package because this filter can be used through the +// `eth_getFilterChanges` polling method that is also used for log filters. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newPendingTransactionFilter +func (api *FiltersAPI) NewPendingTransactionFilter() gethrpc.ID { + api.filtersMu.Lock() + defer api.filtersMu.Unlock() + + if len(api.filters) >= int(api.backend.RPCFilterCap()) { + return gethrpc.ID("error creating pending tx filter: max limit reached") + } + + pendingTxSub, cancelSubs, err := api.events.SubscribePendingTxs() + if err != nil { + // wrap error on the ID + return gethrpc.ID(fmt.Sprintf("error creating pending tx filter: %s", err.Error())) + } + + api.filters[pendingTxSub.ID()] = &filter{ + typ: filters.PendingTransactionsSubscription, + deadline: time.NewTimer(deadlineForInactivity()), + hashes: make([]common.Hash, 0), + s: pendingTxSub, + } + + go func(txsCh <-chan coretypes.ResultEvent, errCh <-chan error) { + defer cancelSubs() + + for { + select { + case ev, ok := <-txsCh: + if !ok { + api.filtersMu.Lock() + delete(api.filters, pendingTxSub.ID()) + api.filtersMu.Unlock() + return + } + + data, ok := ev.Data.(tmtypes.EventDataTx) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", ev.Data)) + continue + } + + tx, err := api.clientCtx.TxConfig.TxDecoder()(data.Tx) + if err != nil { + api.logger.Debug("fail to decode tx", "error", err.Error()) + continue + } + + api.filtersMu.Lock() + if f, found := api.filters[pendingTxSub.ID()]; found { + for _, msg := range tx.GetMsgs() { + ethTx, ok := msg.(*evm.MsgEthereumTx) + if ok { + f.hashes = append(f.hashes, ethTx.AsTransaction().Hash()) + } + } + } + api.filtersMu.Unlock() + case <-errCh: + api.filtersMu.Lock() + delete(api.filters, pendingTxSub.ID()) + api.filtersMu.Unlock() + } + } + }(pendingTxSub.EventCh, pendingTxSub.Error()) + + return pendingTxSub.ID() +} + +// NewPendingTransactions creates a subscription that is triggered each time a +// transaction enters the transaction pool and was signed from one of the +// transactions this nodes manages. +func (api *FiltersAPI) NewPendingTransactions(ctx context.Context) (*gethrpc.Subscription, error) { + api.logger.Debug("eth_newPendingTransactions") + notifier, supported := gethrpc.NotifierFromContext(ctx) + if !supported { + return &gethrpc.Subscription{}, gethrpc.ErrNotificationsUnsupported + } + + rpcSub := notifier.CreateSubscription() + + ctx, cancelFn := context.WithTimeout(context.Background(), deadlineForInactivity()) + defer cancelFn() + + api.events.WithContext(ctx) + + pendingTxSub, cancelSubs, err := api.events.SubscribePendingTxs() + if err != nil { + return nil, err + } + + go func(txsCh <-chan coretypes.ResultEvent) { + defer cancelSubs() + + for { + select { + case ev, ok := <-txsCh: + if !ok { + api.filtersMu.Lock() + delete(api.filters, pendingTxSub.ID()) + api.filtersMu.Unlock() + return + } + + data, ok := ev.Data.(tmtypes.EventDataTx) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", ev.Data)) + continue + } + + tx, err := api.clientCtx.TxConfig.TxDecoder()(data.Tx) + if err != nil { + api.logger.Debug("fail to decode tx", "error", err.Error()) + continue + } + + for _, msg := range tx.GetMsgs() { + ethTx, ok := msg.(*evm.MsgEthereumTx) + if ok { + _ = notifier.Notify(rpcSub.ID, ethTx.AsTransaction().Hash()) // #nosec G703 + } + } + case <-rpcSub.Err(): + pendingTxSub.Unsubscribe(api.events) + return + case <-notifier.Closed(): + pendingTxSub.Unsubscribe(api.events) + return + } + } + }(pendingTxSub.EventCh) + + return rpcSub, err +} + +// NewBlockFilter creates a filter that fetches blocks that are imported into the +// chain. It is part of the filter package since polling goes with +// eth_getFilterChanges. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter +func (api *FiltersAPI) NewBlockFilter() gethrpc.ID { + api.filtersMu.Lock() + defer api.filtersMu.Unlock() + + if len(api.filters) >= int(api.backend.RPCFilterCap()) { + return gethrpc.ID("error creating block filter: max limit reached") + } + + headerSub, cancelSubs, err := api.events.SubscribeNewHeads() + if err != nil { + // wrap error on the ID + return gethrpc.ID(fmt.Sprintf("error creating block filter: %s", err.Error())) + } + + api.filters[headerSub.ID()] = &filter{typ: filters.BlocksSubscription, deadline: time.NewTimer(deadlineForInactivity()), hashes: []common.Hash{}, s: headerSub} + + go func(headersCh <-chan coretypes.ResultEvent, errCh <-chan error) { + defer cancelSubs() + + for { + select { + case ev, ok := <-headersCh: + if !ok { + api.filtersMu.Lock() + delete(api.filters, headerSub.ID()) + api.filtersMu.Unlock() + return + } + + data, ok := ev.Data.(tmtypes.EventDataNewBlockHeader) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", ev.Data)) + continue + } + + api.filtersMu.Lock() + if f, found := api.filters[headerSub.ID()]; found { + f.hashes = append(f.hashes, common.BytesToHash(data.Header.Hash())) + } + api.filtersMu.Unlock() + case <-errCh: + api.filtersMu.Lock() + delete(api.filters, headerSub.ID()) + api.filtersMu.Unlock() + return + } + } + }(headerSub.EventCh, headerSub.Error()) + + return headerSub.ID() +} + +// NewHeads send a notification each time a new block (and thus, block header) is +// added to the chain. +func (api *FiltersAPI) NewHeads(ctx context.Context) (*gethrpc.Subscription, error) { + api.logger.Debug("eth_newHeads") + notifier, supported := gethrpc.NotifierFromContext(ctx) + if !supported { + return &gethrpc.Subscription{}, gethrpc.ErrNotificationsUnsupported + } + + api.events.WithContext(ctx) + rpcSub := notifier.CreateSubscription() + + headersSub, cancelSubs, err := api.events.SubscribeNewHeads() + if err != nil { + return &gethrpc.Subscription{}, err + } + + // Start go routine to continue executing without blocking while the + // goroutine handles incoming events. + // The routine receives a channel that receives block header events as an + // argument. + go func(headersCh <-chan coretypes.ResultEvent) { + // This defer statement ensures the subscription is canceled + // when the goroutine exits. + defer cancelSubs() + + // Listen for block header events and handle them based on the + // type of event received. + for { + select { + case ev, ok := <-headersCh: + if !ok { + headersSub.Unsubscribe(api.events) + return + } + + data, ok := ev.Data.(tmtypes.EventDataNewBlockHeader) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", ev.Data)) + continue + } + + var baseFee *big.Int = nil + bloom, err := ParseBloomFromEvents(data.ResultEndBlock.Events) + if err != nil { + api.logger.Error("failed to parse bloom from end block events") + return + } + header := rpc.EthHeaderFromTendermint(data.Header, bloom, baseFee) + _ = notifier.Notify(rpcSub.ID, header) // #nosec G703 + case <-rpcSub.Err(): + headersSub.Unsubscribe(api.events) + return + case <-notifier.Closed(): + headersSub.Unsubscribe(api.events) + return + } + } + }(headersSub.EventCh) + + return rpcSub, err +} + +// Logs creates a subscription that fires for all new log that match the given +// filter criteria. +// Implements "eth_logs". +func (api *FiltersAPI) Logs( + ctx context.Context, crit filters.FilterCriteria, +) (*gethrpc.Subscription, error) { + api.logger.Debug("eth_logs") + notifier, supported := gethrpc.NotifierFromContext(ctx) + if !supported { + return &gethrpc.Subscription{}, gethrpc.ErrNotificationsUnsupported + } + + api.events.WithContext(ctx) + rpcSub := notifier.CreateSubscription() + + logsSub, cancelSubs, err := api.events.SubscribeLogs(crit) + if err != nil { + return &gethrpc.Subscription{}, err + } + + go func(logsCh <-chan coretypes.ResultEvent) { + defer cancelSubs() + + for { + select { + case ev, ok := <-logsCh: + if !ok { + logsSub.Unsubscribe(api.events) + return + } + + // filter only events from EVM module txs + _, isMsgEthereumTx := ev.Events[evm.TypeUrlEventEthereumTx] + + if !isMsgEthereumTx { + // ignore transaction as it's not from the evm module + return + } + + // get transaction result data + dataTx, ok := ev.Data.(tmtypes.EventDataTx) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", ev.Data)) + continue + } + + txResponse, err := evm.DecodeTxResponse(dataTx.TxResult.Result.Data) + if err != nil { + api.logger.Error("fail to decode tx response", "error", err) + return + } + + logs := FilterLogs(evm.LogsToEthereum(txResponse.Logs), crit.FromBlock, crit.ToBlock, crit.Addresses, crit.Topics) + + for _, log := range logs { + _ = notifier.Notify(rpcSub.ID, log) // #nosec G703 + } + case <-rpcSub.Err(): // client send an unsubscribe request + logsSub.Unsubscribe(api.events) + return + case <-notifier.Closed(): // connection dropped + logsSub.Unsubscribe(api.events) + return + } + } + }(logsSub.EventCh) + + return rpcSub, err +} + +// NewFilter creates a new filter and returns the filter id. It can be +// used to retrieve logs when the state changes. This method cannot be +// used to fetch logs that are already stored in the state. +// +// Default criteria for the from and to block are "latest". +// Using "latest" as block number will return logs for mined blocks. +// Using "pending" as block number returns logs for not yet mined (pending) blocks. +// In case logs are removed (chain reorg) previously returned logs are returned +// again but with the removed property set to true. +// +// In case "fromBlock" > "toBlock" an error is returned. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter +func (api *FiltersAPI) NewFilter(criteria filters.FilterCriteria) (gethrpc.ID, error) { + api.logger.Debug("eth_newFilter") + api.filtersMu.Lock() + defer api.filtersMu.Unlock() + + if len(api.filters) >= int(api.backend.RPCFilterCap()) { + return gethrpc.ID(""), fmt.Errorf("error creating filter: max limit reached") + } + + var ( + filterID = gethrpc.ID("") + err error + ) + + logsSub, cancelSubs, err := api.events.SubscribeLogs(criteria) + if err != nil { + return gethrpc.ID(""), err + } + + filterID = logsSub.ID() + + api.filters[filterID] = &filter{ + typ: filters.LogsSubscription, + crit: criteria, + deadline: time.NewTimer(deadlineForInactivity()), + hashes: []common.Hash{}, + s: logsSub, + } + + go func(eventCh <-chan coretypes.ResultEvent) { + defer cancelSubs() + + for { + select { + case ev, ok := <-eventCh: + if !ok { + api.filtersMu.Lock() + delete(api.filters, filterID) + api.filtersMu.Unlock() + return + } + dataTx, ok := ev.Data.(tmtypes.EventDataTx) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", ev.Data)) + continue + } + + txResponse, err := evm.DecodeTxResponse(dataTx.TxResult.Result.Data) + if err != nil { + api.logger.Error("fail to decode tx response", "error", err) + return + } + + logs := FilterLogs(evm.LogsToEthereum(txResponse.Logs), criteria.FromBlock, criteria.ToBlock, criteria.Addresses, criteria.Topics) + + api.filtersMu.Lock() + if f, found := api.filters[filterID]; found { + f.logs = append(f.logs, logs...) + } + api.filtersMu.Unlock() + case <-logsSub.Error(): + api.filtersMu.Lock() + delete(api.filters, filterID) + api.filtersMu.Unlock() + return + } + } + }(logsSub.EventCh) + + return filterID, err +} + +// GetLogs returns logs matching the given argument that are stored within the state. +// This function implements the "eth_getLogs" JSON-RPC service method. +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs +func (api *FiltersAPI) GetLogs( + ctx context.Context, crit filters.FilterCriteria, +) ([]*gethcore.Log, error) { + var filter *Filter + if crit.BlockHash != nil { + // Block filter requested, construct a single-shot filter + filter = NewBlockFilter(api.logger, *api.backend, crit) + } else { + // Convert the RPC block numbers into internal representations + begin := gethrpc.LatestBlockNumber.Int64() + if crit.FromBlock != nil { + begin = crit.FromBlock.Int64() + } + end := gethrpc.LatestBlockNumber.Int64() + if crit.ToBlock != nil { + end = crit.ToBlock.Int64() + } + // Construct the range filter + filter = NewRangeFilter(api.logger, *api.backend, begin, end, crit.Addresses, crit.Topics) + } + + api.logger.Debug("eth_getLogs", + "from_block", filter.criteria.FromBlock.String(), + "to_block", filter.criteria.ToBlock.String(), + "time", time.Now().UTC(), + ) + + // Run the filter and return all the logs + logs, err := filter.Logs(ctx, int(api.backend.RPCLogsCap()), int64(api.backend.RPCBlockRangeCap())) + if err != nil { + return nil, err + } + + return returnLogs(logs), err +} + +// UninstallFilter removes the filter with the given filter id. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter +func (api *FiltersAPI) UninstallFilter(id gethrpc.ID) bool { + api.filtersMu.Lock() + f, found := api.filters[id] + if found { + delete(api.filters, id) + } + api.filtersMu.Unlock() + + if !found { + return false + } + f.s.Unsubscribe(api.events) + return true +} + +// GetFilterLogs returns the logs for the filter with the given id. +// If the filter could not be found an empty array of logs is returned. +// +// This function implements the "eth_getFilterLogs" JSON-RPC service method. +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs +func (api *FiltersAPI) GetFilterLogs(ctx context.Context, id gethrpc.ID) ([]*gethcore.Log, error) { + api.logger.Debug("eth_getFilterLogs") + api.filtersMu.Lock() + f, found := api.filters[id] + api.filtersMu.Unlock() + + if !found { + return returnLogs(nil), fmt.Errorf("filter %s not found", id) + } + + if f.typ != filters.LogsSubscription { + return returnLogs(nil), fmt.Errorf("filter %s doesn't have a LogsSubscription type: got %d", id, f.typ) + } + + var filter *Filter + if f.crit.BlockHash != nil { + // Block filter requested, construct a single-shot filter + filter = NewBlockFilter(api.logger, *api.backend, f.crit) + } else { + // Convert the RPC block numbers into internal representations + begin := gethrpc.LatestBlockNumber.Int64() + if f.crit.FromBlock != nil { + begin = f.crit.FromBlock.Int64() + } + end := gethrpc.LatestBlockNumber.Int64() + if f.crit.ToBlock != nil { + end = f.crit.ToBlock.Int64() + } + // Construct the range filter + filter = NewRangeFilter(api.logger, *api.backend, begin, end, f.crit.Addresses, f.crit.Topics) + } + // Run the filter and return all the logs + logs, err := filter.Logs(ctx, int(api.backend.RPCLogsCap()), int64(api.backend.RPCBlockRangeCap())) + if err != nil { + return nil, err + } + return returnLogs(logs), nil +} + +// GetFilterChanges returns the logs for the filter with the given id since last +// time it was called. This can be used for polling. +// +// For pending transaction and block filters the result is []common.Hash. +// (pending)Log filters return []Log. +// +// This function implements the "eth_getFilterChanges" JSON-RPC service method. +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges +func (api *FiltersAPI) GetFilterChanges(id gethrpc.ID) (any, error) { + api.logger.Debug("eth_getFilterChanges") + api.filtersMu.Lock() + defer api.filtersMu.Unlock() + + f, found := api.filters[id] + if !found { + return nil, fmt.Errorf("filter %s not found", id) + } + + if !f.deadline.Stop() { + // timer expired but filter is not yet removed in timeout loop + // receive timer value and reset timer + <-f.deadline.C + } + f.deadline.Reset(deadlineForInactivity()) + + switch f.typ { + case filters.PendingTransactionsSubscription, filters.BlocksSubscription: + hashes := f.hashes + f.hashes = nil + return returnHashes(hashes), nil + case filters.LogsSubscription, filters.MinedAndPendingLogsSubscription: + logs := make([]*gethcore.Log, len(f.logs)) + copy(logs, f.logs) + f.logs = []*gethcore.Log{} + return returnLogs(logs), nil + default: + return nil, fmt.Errorf("invalid filter %s type %d", id, f.typ) + } +} diff --git a/eth/rpc/rpcapi/event_subscriber.go b/eth/rpc/rpcapi/event_subscriber.go new file mode 100644 index 000000000..7f038dbdc --- /dev/null +++ b/eth/rpc/rpcapi/event_subscriber.go @@ -0,0 +1,327 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/pkg/errors" + + tmjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/libs/log" + tmquery "github.com/cometbft/cometbft/libs/pubsub/query" + coretypes "github.com/cometbft/cometbft/rpc/core/types" + rpcclient "github.com/cometbft/cometbft/rpc/jsonrpc/client" + tmtypes "github.com/cometbft/cometbft/types" + + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" + gethrpc "github.com/ethereum/go-ethereum/rpc" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth/rpc/pubsub" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +var ( + txEventsQuery = tmtypes.QueryForEvent(tmtypes.EventTx).String() + evmEventsQuery = tmquery.MustParse(fmt.Sprintf("%s='%s' AND %s.%s='%s'", + tmtypes.EventTypeKey, + tmtypes.EventTx, + sdk.EventTypeMessage, + sdk.AttributeKeyModule, evm.ModuleName)).String() + headerEventsQuery = tmtypes.QueryForEvent(tmtypes.EventNewBlockHeader).String() +) + +// EventSubscriber creates subscriptions, processes events and broadcasts them to the +// subscription which match the subscription criteria using the Tendermint's RPC +// client. +type EventSubscriber struct { + Logger log.Logger + Ctx context.Context + TmWSClient *rpcclient.WSClient + + // light client mode + LightMode bool + + Index FilterIndex + TopicChans map[string]chan<- coretypes.ResultEvent + IndexMux *sync.RWMutex + + // Channels + Install chan *Subscription // install filter for event notification + Uninstall chan *Subscription // remove filter for event notification + EventBus pubsub.EventBus +} + +// NewEventSubscriber creates a new manager that listens for event on the given mux, +// parses and filters them. It uses the all map to retrieve filter changes. The +// work loop holds its own index that is used to forward events to filters. +// +// The returned manager has a loop that needs to be stopped with the Stop function +// or by stopping the given mux. +func NewEventSubscriber( + logger log.Logger, + tmWSClient *rpcclient.WSClient, +) *EventSubscriber { + index := make(FilterIndex) + for i := filters.UnknownSubscription; i < filters.LastIndexSubscription; i++ { + index[i] = make(map[gethrpc.ID]*Subscription) + } + + es := &EventSubscriber{ + Logger: logger, + Ctx: context.Background(), + TmWSClient: tmWSClient, + LightMode: false, + Index: index, + TopicChans: make(map[string]chan<- coretypes.ResultEvent, len(index)), + IndexMux: new(sync.RWMutex), + Install: make(chan *Subscription), + Uninstall: make(chan *Subscription), + EventBus: pubsub.NewEventBus(), + } + + go es.EventLoop() + go es.consumeEvents() + return es +} + +// WithContext sets a new context to the EventSystem. This is required to set a timeout context when +// a new filter is intantiated. +func (es *EventSubscriber) WithContext(ctx context.Context) { + es.Ctx = ctx +} + +// subscribe performs a new event subscription to a given Tendermint event. +// The subscription creates a unidirectional receive event channel to receive the ResultEvent. +func (es *EventSubscriber) subscribe(sub *Subscription) (*Subscription, pubsub.UnsubscribeFunc, error) { + var ( + err error + cancelFn context.CancelFunc + ) + + ctx, cancelFn := context.WithCancel(context.Background()) + defer cancelFn() + + existingSubs := es.EventBus.Topics() + for _, topic := range existingSubs { + if topic == sub.Event { + eventCh, unsubFn, err := es.EventBus.Subscribe(sub.Event) + if err != nil { + err := errors.Wrapf(err, "failed to subscribe to topic: %s", sub.Event) + return nil, nil, err + } + + sub.EventCh = eventCh + return sub, unsubFn, nil + } + } + + switch sub.Typ { + case filters.LogsSubscription: + err = es.TmWSClient.Subscribe(ctx, sub.Event) + case filters.BlocksSubscription: + err = es.TmWSClient.Subscribe(ctx, sub.Event) + case filters.PendingTransactionsSubscription: + err = es.TmWSClient.Subscribe(ctx, sub.Event) + default: + err = fmt.Errorf("invalid filter subscription type %d", sub.Typ) + } + + if err != nil { + sub.ErrCh <- err + return nil, nil, err + } + + // wrap events in a go routine to prevent blocking + es.Install <- sub + <-sub.Installed + + eventCh, unsubFn, err := es.EventBus.Subscribe(sub.Event) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to subscribe to topic after installed: %s", sub.Event) + } + + sub.EventCh = eventCh + return sub, unsubFn, nil +} + +// SubscribeLogs creates a subscription that will write all logs matching the +// given criteria to the given logs channel. Default value for the from and to +// block is "latest". If the fromBlock > toBlock an error is returned. +func (es *EventSubscriber) SubscribeLogs(crit filters.FilterCriteria) (*Subscription, pubsub.UnsubscribeFunc, error) { + var from, to gethrpc.BlockNumber + if crit.FromBlock == nil { + from = gethrpc.LatestBlockNumber + } else { + from = gethrpc.BlockNumber(crit.FromBlock.Int64()) + } + if crit.ToBlock == nil { + to = gethrpc.LatestBlockNumber + } else { + to = gethrpc.BlockNumber(crit.ToBlock.Int64()) + } + + switch { + // only interested in new mined logs, mined logs within a specific block range, or + // logs from a specific block number to new mined blocks + case (from == gethrpc.LatestBlockNumber && to == gethrpc.LatestBlockNumber), + (from >= 0 && to >= 0 && to >= from), + (from >= 0 && to == gethrpc.LatestBlockNumber): + + // Create a subscription that will write all logs matching the + // given criteria to the given logs channel. + sub := &Subscription{ + Id: gethrpc.NewID(), + Typ: filters.LogsSubscription, + Event: evmEventsQuery, + logsCrit: crit, + Created: time.Now().UTC(), + Logs: make(chan []*gethcore.Log), + Installed: make(chan struct{}, 1), + ErrCh: make(chan error, 1), + } + return es.subscribe(sub) + + default: + return nil, nil, fmt.Errorf("invalid from and to block combination: from > to (%d > %d)", from, to) + } +} + +// SubscribeNewHeads subscribes to new block headers events. +func (es EventSubscriber) SubscribeNewHeads() (*Subscription, pubsub.UnsubscribeFunc, error) { + sub := &Subscription{ + Id: gethrpc.NewID(), + Typ: filters.BlocksSubscription, + Event: headerEventsQuery, + Created: time.Now().UTC(), + Headers: make(chan *gethcore.Header), + Installed: make(chan struct{}, 1), + ErrCh: make(chan error, 1), + } + return es.subscribe(sub) +} + +// SubscribePendingTxs subscribes to new pending transactions events from the mempool. +func (es EventSubscriber) SubscribePendingTxs() (*Subscription, pubsub.UnsubscribeFunc, error) { + sub := &Subscription{ + Id: gethrpc.NewID(), + Typ: filters.PendingTransactionsSubscription, + Event: txEventsQuery, + Created: time.Now().UTC(), + Hashes: make(chan []common.Hash), + Installed: make(chan struct{}, 1), + ErrCh: make(chan error, 1), + } + return es.subscribe(sub) +} + +type FilterIndex map[filters.Type]map[gethrpc.ID]*Subscription + +// EventLoop (un)installs filters and processes mux events. +func (es *EventSubscriber) EventLoop() { + for { + select { + case f := <-es.Install: + es.IndexMux.Lock() + es.Index[f.Typ][f.Id] = f + ch := make(chan coretypes.ResultEvent) + if err := es.EventBus.AddTopic(f.Event, ch); err != nil { + es.Logger.Error("failed to add event topic to event bus", "topic", f.Event, "error", err.Error()) + } else { + es.TopicChans[f.Event] = ch + } + es.IndexMux.Unlock() + close(f.Installed) + case f := <-es.Uninstall: + es.IndexMux.Lock() + delete(es.Index[f.Typ], f.Id) + + var channelInUse bool + // #nosec G705 + for _, sub := range es.Index[f.Typ] { + if sub.Event == f.Event { + channelInUse = true + break + } + } + + // remove topic only when channel is not used by other subscriptions + if !channelInUse { + if err := es.TmWSClient.Unsubscribe(es.Ctx, f.Event); err != nil { + es.Logger.Error("failed to unsubscribe from query", "query", f.Event, "error", err.Error()) + } + + ch, ok := es.TopicChans[f.Event] + if ok { + es.EventBus.RemoveTopic(f.Event) + close(ch) + delete(es.TopicChans, f.Event) + } + } + + es.IndexMux.Unlock() + close(f.ErrCh) + } + } +} + +func (es *EventSubscriber) consumeEvents() { + for { + for rpcResp := range es.TmWSClient.ResponsesCh { + var ev coretypes.ResultEvent + + if rpcResp.Error != nil { + time.Sleep(5 * time.Second) + continue + } else if err := tmjson.Unmarshal(rpcResp.Result, &ev); err != nil { + es.Logger.Error("failed to JSON unmarshal ResponsesCh result event", "error", err.Error()) + continue + } + + if len(ev.Query) == 0 { + // skip empty responses + continue + } + + es.IndexMux.RLock() + ch, ok := es.TopicChans[ev.Query] + es.IndexMux.RUnlock() + if !ok { + es.Logger.Debug("channel for subscription not found", "topic", ev.Query) + es.Logger.Debug("list of available channels", "channels", es.EventBus.Topics()) + continue + } + + // gracefully handle lagging subscribers + t := time.NewTimer(time.Second) + select { + case <-t.C: + es.Logger.Debug("dropped event during lagging subscription", "topic", ev.Query) + case ch <- ev: + } + } + + time.Sleep(time.Second) + } +} + +func MakeSubscription(id, event string) *Subscription { + return &Subscription{ + Id: gethrpc.ID(id), + Typ: filters.LogsSubscription, + Event: event, + Created: time.Now(), + Logs: make(chan []*gethcore.Log), + Hashes: make(chan []common.Hash), + Headers: make(chan *gethcore.Header), + Installed: make(chan struct{}), + EventCh: make(chan coretypes.ResultEvent), + ErrCh: make(chan error), + } +} diff --git a/eth/rpc/rpcapi/event_subscriber_test.go b/eth/rpc/rpcapi/event_subscriber_test.go new file mode 100644 index 000000000..dc14b092e --- /dev/null +++ b/eth/rpc/rpcapi/event_subscriber_test.go @@ -0,0 +1,127 @@ +package rpcapi_test + +import ( + "context" + "os" + "sync" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + gogoproto "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/suite" + + "github.com/cometbft/cometbft/libs/log" + coretypes "github.com/cometbft/cometbft/rpc/core/types" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/rpc" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/rpc/pubsub" + "github.com/NibiruChain/nibiru/v2/eth/rpc/rpcapi" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +type Suite struct { + suite.Suite +} + +func TestEventSubscriber(t *testing.T) { + index := make(rpcapi.FilterIndex) + for i := filters.UnknownSubscription; i < filters.LastIndexSubscription; i++ { + index[i] = make(map[rpc.ID]*rpcapi.Subscription) + } + es := &rpcapi.EventSubscriber{ + Logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)), + Ctx: context.Background(), + LightMode: false, + Index: index, + TopicChans: make(map[string]chan<- coretypes.ResultEvent, len(index)), + IndexMux: new(sync.RWMutex), + Install: make(chan *rpcapi.Subscription), + Uninstall: make(chan *rpcapi.Subscription), + EventBus: pubsub.NewEventBus(), + } + go es.EventLoop() + + event := "event" + sub := rpcapi.MakeSubscription("1", event) + es.Install <- sub + <-sub.Installed + ch, ok := es.TopicChans[sub.Event] + if !ok { + t.Error("expect topic channel exist") + } + + sub = rpcapi.MakeSubscription("2", event) + es.Install <- sub + <-sub.Installed + newCh, ok := es.TopicChans[sub.Event] + if !ok { + t.Error("expect topic channel exist") + } + + if newCh != ch { + t.Error("expect topic channel unchanged") + } +} + +func (s *Suite) TestParseBloomFromEvents() { + for _, tc := range []struct { + name string + endBlockEvents func() (gethcore.Bloom, []abci.Event) + wantErr string + }{ + { + name: "happy: empty events", + endBlockEvents: func() (gethcore.Bloom, []abci.Event) { + return *new(gethcore.Bloom), []abci.Event{} + }, + wantErr: "", + }, + { + name: "happy: events with bloom included", + endBlockEvents: func() (gethcore.Bloom, []abci.Event) { + deps := evmtest.NewTestDeps() + + // populate valid bloom + bloom := gethcore.Bloom{} + dummyBz := []byte("dummybloom") + copy(bloom[:], dummyBz) + + err := deps.Ctx.EventManager().EmitTypedEvents( + &evm.EventTransfer{}, + &evm.EventBlockBloom{ + Bloom: eth.BloomToHex(bloom), + }, + ) + s.NoError(err, "emitting bloom event failed") + + abciEvents := deps.Ctx.EventManager().ABCIEvents() + + bloomEvent := new(evm.EventBlockBloom) + bloomEventType := gogoproto.MessageName(bloomEvent) + + err = testutil.AssertEventPresent(deps.Ctx.EventManager().Events(), bloomEventType) + s.Require().NoError(err) + + return bloom, abciEvents + }, + wantErr: "", + }, + } { + s.Run(tc.name, func() { + wantBloom, events := tc.endBlockEvents() + bloom, err := rpcapi.ParseBloomFromEvents(events) + + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + + s.Require().Equal(wantBloom, bloom) + }) + } +} diff --git a/eth/rpc/rpcapi/filter_utils.go b/eth/rpc/rpcapi/filter_utils.go new file mode 100644 index 000000000..41983b84d --- /dev/null +++ b/eth/rpc/rpcapi/filter_utils.go @@ -0,0 +1,131 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "math/big" + + "cosmossdk.io/errors" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + gogoproto "github.com/cosmos/gogoproto/proto" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// FilterLogs creates a slice of logs matching the given criteria. +// [] -> anything +// [A] -> A in first position of log topics, anything after +// [null, B] -> anything in first position, B in second position +// [A, B] -> A in first position and B in second position +// [[A, B], [A, B]] -> A or B in first position, A or B in second position +func FilterLogs(logs []*gethcore.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*gethcore.Log { + var ret []*gethcore.Log +Logs: + for _, log := range logs { + if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { + continue + } + if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { + continue + } + if len(addresses) > 0 && !includes(addresses, log.Address) { + continue + } + // If the to filtered topics is greater than the amount of topics in logs, skip. + if len(topics) > len(log.Topics) { + continue + } + for i, sub := range topics { + match := len(sub) == 0 // empty rule set == wildcard + for _, topic := range sub { + if log.Topics[i] == topic { + match = true + break + } + } + if !match { + continue Logs + } + } + ret = append(ret, log) + } + return ret +} + +func includes(addresses []common.Address, a common.Address) bool { + for _, addr := range addresses { + if addr == a { + return true + } + } + + return false +} + +// https://github.com/ethereum/go-ethereum/blob/v1.10.14/eth/filters/filter.go#L321 +func bloomFilter(bloom gethcore.Bloom, addresses []common.Address, topics [][]common.Hash) bool { + if len(addresses) > 0 { + var included bool + for _, addr := range addresses { + if gethcore.BloomLookup(bloom, addr) { + included = true + break + } + } + if !included { + return false + } + } + + for _, sub := range topics { + included := len(sub) == 0 // empty rule set == wildcard + for _, topic := range sub { + if gethcore.BloomLookup(bloom, topic) { + included = true + break + } + } + if !included { + return false + } + } + return true +} + +// returnHashes is a helper that will return an empty hash array case the given hash array is nil, +// otherwise the given hashes array is returned. +func returnHashes(hashes []common.Hash) []common.Hash { + if hashes == nil { + return []common.Hash{} + } + return hashes +} + +// returnLogs is a helper that will return an empty log array in case the given logs array is nil, +// otherwise the given logs array is returned. +func returnLogs(logs []*gethcore.Log) []*gethcore.Log { + if logs == nil { + return []*gethcore.Log{} + } + return logs +} + +// ParseBloomFromEvents iterates through the slice of events +func ParseBloomFromEvents(events []abci.Event) (bloom gethcore.Bloom, err error) { + bloomEventType := gogoproto.MessageName(new(evm.EventBlockBloom)) + for _, event := range events { + if event.Type != bloomEventType { + continue + } + bloomTypedEvent, err := evm.EventBlockBloomFromABCIEvent(event) + if err != nil { + return bloom, errors.Wrapf( + err, "failed to parse event of type %s", bloomEventType) + } + return eth.BloomFromHex(bloomTypedEvent.Bloom) + } + return bloom, err +} diff --git a/eth/rpc/rpcapi/filters.go b/eth/rpc/rpcapi/filters.go new file mode 100644 index 000000000..850e7fdfd --- /dev/null +++ b/eth/rpc/rpcapi/filters.go @@ -0,0 +1,273 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "context" + "encoding/binary" + "fmt" + "math/big" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" + rpcbackend "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + + "github.com/cometbft/cometbft/libs/log" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/pkg/errors" + + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/filters" +) + +// BloomIV represents the bit indexes and value inside the bloom filter that belong +// to some key. +type BloomIV struct { + I [3]uint + V [3]byte +} + +// Filter can be used to retrieve and filter logs. +type Filter struct { + logger log.Logger + backend rpcbackend.Backend + criteria filters.FilterCriteria + + bloomFilters [][]BloomIV // Filter the system is matching for +} + +// NewBlockFilter creates a new filter which directly inspects the contents of +// a block to figure out whether it is interesting or not. +func NewBlockFilter(logger log.Logger, backend rpcbackend.Backend, criteria filters.FilterCriteria) *Filter { + // Create a generic filter and convert it into a block filter + return newFilter(logger, backend, criteria, nil) +} + +// NewRangeFilter creates a new filter which uses a bloom filter on blocks to +// figure out whether a particular block is interesting or not. +func NewRangeFilter(logger log.Logger, backend rpcbackend.Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { + // Flatten the address and topic filter clauses into a single bloombits filter + // system. Since the bloombits are not positional, nil topics are permitted, + // which get flattened into a nil byte slice. + var filtersBz [][][]byte //nolint: prealloc + if len(addresses) > 0 { + filter := make([][]byte, len(addresses)) + for i, address := range addresses { + filter[i] = address.Bytes() + } + filtersBz = append(filtersBz, filter) + } + + for _, topicList := range topics { + filter := make([][]byte, len(topicList)) + for i, topic := range topicList { + filter[i] = topic.Bytes() + } + filtersBz = append(filtersBz, filter) + } + + // Create a generic filter and convert it into a range filter + criteria := filters.FilterCriteria{ + FromBlock: big.NewInt(begin), + ToBlock: big.NewInt(end), + Addresses: addresses, + Topics: topics, + } + + return newFilter(logger, backend, criteria, createBloomFilters(filtersBz, logger)) +} + +// newFilter returns a new Filter +func newFilter( + logger log.Logger, + backend rpcbackend.Backend, + criteria filters.FilterCriteria, + bloomFilters [][]BloomIV, +) *Filter { + return &Filter{ + logger: logger, + backend: backend, + criteria: criteria, + bloomFilters: bloomFilters, + } +} + +const ( + maxToOverhang = 600 +) + +// Logs searches the blockchain for matching log entries, returning all from the +// first block that contains matches, updating the start of the filter accordingly. +func (f *Filter) Logs(_ context.Context, logLimit int, blockLimit int64) ([]*gethcore.Log, error) { + logs := []*gethcore.Log{} + var err error + + // If we're doing singleton block filtering, execute and return + if f.criteria.BlockHash != nil && *f.criteria.BlockHash != (common.Hash{}) { + resBlock, err := f.backend.TendermintBlockByHash(*f.criteria.BlockHash) + if err != nil { + return nil, fmt.Errorf("failed to fetch header by hash %s: %w", f.criteria.BlockHash, err) + } + + blockRes, err := f.backend.TendermintBlockResultByNumber(&resBlock.Block.Height) + if err != nil { + f.logger.Debug("failed to fetch block result from Tendermint", "height", resBlock.Block.Height, "error", err.Error()) + return nil, nil + } + + bloom, err := f.backend.BlockBloom(blockRes) + if err != nil { + return nil, err + } + + return f.blockLogs(blockRes, bloom) + } + + // Figure out the limits of the filter range + header, err := f.backend.HeaderByNumber(rpc.EthLatestBlockNumber) + if err != nil { + return nil, fmt.Errorf("failed to fetch header by number (latest): %w", err) + } + + if header == nil || header.Number == nil { + f.logger.Debug("header not found or has no number") + return nil, nil + } + + head := header.Number.Int64() + if f.criteria.FromBlock.Int64() < 0 { + f.criteria.FromBlock = big.NewInt(head) + } else if f.criteria.FromBlock.Int64() == 0 { + f.criteria.FromBlock = big.NewInt(1) + } + if f.criteria.ToBlock.Int64() < 0 { + f.criteria.ToBlock = big.NewInt(head) + } else if f.criteria.ToBlock.Int64() == 0 { + f.criteria.ToBlock = big.NewInt(1) + } + + if f.criteria.ToBlock.Int64()-f.criteria.FromBlock.Int64() > blockLimit { + return nil, fmt.Errorf("maximum [from, to] blocks distance: %d", blockLimit) + } + + // check bounds + if f.criteria.FromBlock.Int64() > head { + return []*gethcore.Log{}, nil + } else if f.criteria.ToBlock.Int64() > head+maxToOverhang { + f.criteria.ToBlock = big.NewInt(head + maxToOverhang) + } + + from := f.criteria.FromBlock.Int64() + to := f.criteria.ToBlock.Int64() + + for height := from; height <= to; height++ { + blockRes, err := f.backend.TendermintBlockResultByNumber(&height) + if err != nil { + f.logger.Debug("failed to fetch block result from Tendermint", "height", height, "error", err.Error()) + return nil, nil + } + + bloom, err := f.backend.BlockBloom(blockRes) + if err != nil { + return nil, err + } + + filtered, err := f.blockLogs(blockRes, bloom) + if err != nil { + return nil, errors.Wrapf(err, "failed to fetch block by number %d", height) + } + + // check logs limit + if len(logs)+len(filtered) > logLimit { + return nil, fmt.Errorf("query returned more than %d results", logLimit) + } + logs = append(logs, filtered...) + } + return logs, nil +} + +// blockLogs returns the logs matching the filter criteria within a single block. +func (f *Filter) blockLogs(blockRes *tmrpctypes.ResultBlockResults, bloom gethcore.Bloom) ([]*gethcore.Log, error) { + if !bloomFilter(bloom, f.criteria.Addresses, f.criteria.Topics) { + return []*gethcore.Log{}, nil + } + + logsList, err := rpcbackend.GetLogsFromBlockResults(blockRes) + if err != nil { + return []*gethcore.Log{}, errors.Wrapf(err, "failed to fetch logs block number %d", blockRes.Height) + } + + unfiltered := make([]*gethcore.Log, 0) + for _, logs := range logsList { + unfiltered = append(unfiltered, logs...) + } + + logs := FilterLogs(unfiltered, nil, nil, f.criteria.Addresses, f.criteria.Topics) + if len(logs) == 0 { + return []*gethcore.Log{}, nil + } + + return logs, nil +} + +func createBloomFilters(filters [][][]byte, logger log.Logger) [][]BloomIV { + bloomFilters := make([][]BloomIV, 0) + for _, filter := range filters { + // Gather the bit indexes of the filter rule, special casing the nil filter + if len(filter) == 0 { + continue + } + bloomIVs := make([]BloomIV, len(filter)) + + // Transform the filter rules (the addresses and topics) to the bloom index and value arrays + // So it can be used to compare with the bloom of the block header. If the rule has any nil + // clauses. The rule will be ignored. + for i, clause := range filter { + if clause == nil { + bloomIVs = nil + break + } + + iv, err := calcBloomIVs(clause) + if err != nil { + bloomIVs = nil + logger.Error("calcBloomIVs error: %v", err) + break + } + + bloomIVs[i] = iv + } + // Accumulate the filter rules if no nil rule was within + if bloomIVs != nil { + bloomFilters = append(bloomFilters, bloomIVs) + } + } + return bloomFilters +} + +// calcBloomIVs returns BloomIV for the given data, +// revised from https://github.com/ethereum/go-ethereum/blob/401354976bb44f0ad4455ca1e0b5c0dc31d9a5f5/core/types/bloom9.go#L139 +func calcBloomIVs(data []byte) (BloomIV, error) { + hashbuf := make([]byte, 6) + biv := BloomIV{} + + sha := crypto.NewKeccakState() + sha.Reset() + if _, err := sha.Write(data); err != nil { + return BloomIV{}, err + } + if _, err := sha.Read(hashbuf); err != nil { + return BloomIV{}, err + } + + // The actual bits to flip + biv.V[0] = byte(1 << (hashbuf[1] & 0x7)) + biv.V[1] = byte(1 << (hashbuf[3] & 0x7)) + biv.V[2] = byte(1 << (hashbuf[5] & 0x7)) + // The indices for the bytes to OR in + biv.I[0] = gethcore.BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf)&0x7ff)>>3) - 1 + biv.I[1] = gethcore.BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1 + biv.I[2] = gethcore.BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1 + + return biv, nil +} diff --git a/eth/rpc/rpcapi/net_api.go b/eth/rpc/rpcapi/net_api.go new file mode 100644 index 000000000..029aacc38 --- /dev/null +++ b/eth/rpc/rpcapi/net_api.go @@ -0,0 +1,56 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "context" + "fmt" + + rpcclient "github.com/cometbft/cometbft/rpc/client" + "github.com/cosmos/cosmos-sdk/client" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// NetAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. +type NetAPI struct { + networkVersion uint64 + // TODO: epic: test(eth-rpc): "github.com/NibiruChain/nibiru/v2/x/common/testutil/cli" + // Validator.RPCClient should be used to test APIs that depend on the CometBFT + // RPC client. + tmClient rpcclient.Client +} + +// NewImplNetAPI creates an instance of the public Net Web3 API. +func NewImplNetAPI(clientCtx client.Context) *NetAPI { + chainID := eth.ParseEthChainID(clientCtx.ChainID) + + return &NetAPI{ + networkVersion: chainID.Uint64(), + tmClient: clientCtx.Client.(rpcclient.Client), + } +} + +// Version returns the current ethereum protocol version. +func (s *NetAPI) Version() string { + return fmt.Sprintf("%d", s.networkVersion) +} + +// Listening returns if client is actively listening for network connections. +func (s *NetAPI) Listening() bool { + ctx := context.Background() + netInfo, err := s.tmClient.NetInfo(ctx) + if err != nil { + return false + } + return netInfo.Listening +} + +// PeerCount returns the number of peers currently connected to the client. +func (s *NetAPI) PeerCount() int { + ctx := context.Background() + netInfo, err := s.tmClient.NetInfo(ctx) + if err != nil { + return 0 + } + return len(netInfo.Peers) +} diff --git a/eth/rpc/rpcapi/net_api_test.go b/eth/rpc/rpcapi/net_api_test.go new file mode 100644 index 000000000..6b54f5094 --- /dev/null +++ b/eth/rpc/rpcapi/net_api_test.go @@ -0,0 +1,13 @@ +package rpcapi_test + +import ( + "github.com/NibiruChain/nibiru/v2/app/appconst" +) + +func (s *NodeSuite) TestNetNamespace() { + api := s.val.EthRpc_NET + s.Require().True(api.Listening()) + s.EqualValues( + appconst.GetEthChainID(s.val.ClientCtx.ChainID).String(), api.Version()) + s.Equal(0, api.PeerCount()) +} diff --git a/eth/rpc/rpcapi/subscription.go b/eth/rpc/rpcapi/subscription.go new file mode 100644 index 000000000..3912216ff --- /dev/null +++ b/eth/rpc/rpcapi/subscription.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "time" + + coretypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/rpc" +) + +// Subscription defines a wrapper for the private subscription +type Subscription struct { + Id rpc.ID + Typ filters.Type + Event string + Created time.Time + logsCrit filters.FilterCriteria + Logs chan []*gethcore.Log + Hashes chan []common.Hash + Headers chan *gethcore.Header + Installed chan struct{} // closed when the filter is installed + // Consensus result event channel + EventCh <-chan coretypes.ResultEvent + ErrCh chan error +} + +// ID returns the underlying subscription RPC identifier. +func (s Subscription) ID() rpc.ID { + return s.Id +} + +// Unsubscribe from the current subscription to Tendermint Websocket. It sends an error to the +// subscription error channel if unsubscribe fails. +func (s *Subscription) Unsubscribe(es *EventSubscriber) { + go func() { + uninstallLoop: + for { + // write uninstall request and consume logs/hashes. This prevents + // the eventLoop broadcast method to deadlock when writing to the + // filter event channel while the subscription loop is waiting for + // this method to return (and thus not reading these events). + select { + case es.Uninstall <- s: + break uninstallLoop + case <-s.Logs: + case <-s.Hashes: + case <-s.Headers: + } + } + }() +} + +// Error returns the error channel +func (s *Subscription) Error() <-chan error { + return s.ErrCh +} diff --git a/eth/rpc/rpcapi/txpool_api.go b/eth/rpc/rpcapi/txpool_api.go new file mode 100644 index 000000000..ff672d6fe --- /dev/null +++ b/eth/rpc/rpcapi/txpool_api.go @@ -0,0 +1,54 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "github.com/cometbft/cometbft/libs/log" + + "github.com/ethereum/go-ethereum/common/hexutil" + + "github.com/NibiruChain/nibiru/v2/eth/rpc" +) + +// TxPoolAPI offers and API for the transaction pool. It only operates on data +// that is non-confidential. +type TxPoolAPI struct { + logger log.Logger +} + +// NewImplTxPoolAPI creates a new tx pool service that gives information about the transaction pool. +func NewImplTxPoolAPI(logger log.Logger) *TxPoolAPI { + return &TxPoolAPI{ + logger: logger.With("module", "txpool"), + } +} + +// Content returns the transactions contained within the transaction pool +func (api *TxPoolAPI) Content() ( + map[string]map[string]map[string]*rpc.EthTxJsonRPC, error, +) { + api.logger.Debug("txpool_content") + content := map[string]map[string]map[string]*rpc.EthTxJsonRPC{ + "pending": make(map[string]map[string]*rpc.EthTxJsonRPC), + "queued": make(map[string]map[string]*rpc.EthTxJsonRPC), + } + return content, nil +} + +// Inspect returns the content of the transaction pool and flattens it into an +func (api *TxPoolAPI) Inspect() (map[string]map[string]map[string]string, error) { + api.logger.Debug("txpool_inspect") + content := map[string]map[string]map[string]string{ + "pending": make(map[string]map[string]string), + "queued": make(map[string]map[string]string), + } + return content, nil +} + +// Status returns the number of pending and queued transaction in the pool. +func (api *TxPoolAPI) Status() map[string]hexutil.Uint { + api.logger.Debug("txpool_status") + return map[string]hexutil.Uint{ + "pending": hexutil.Uint(0), + "queued": hexutil.Uint(0), + } +} diff --git a/eth/rpc/rpcapi/web3_api.go b/eth/rpc/rpcapi/web3_api.go new file mode 100644 index 000000000..aefd3d059 --- /dev/null +++ b/eth/rpc/rpcapi/web3_api.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + appconst "github.com/NibiruChain/nibiru/v2/app/appconst" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" +) + +// APIWeb3 is the web3_ prefixed set of APIs in the Web3 JSON-RPC spec. +type APIWeb3 struct{} + +// NewImplWeb3API creates an instance of the Web3 API. +func NewImplWeb3API() *APIWeb3 { + return &APIWeb3{} +} + +// ClientVersion returns the client version in the Web3 user agent format. +func (a *APIWeb3) ClientVersion() string { + return appconst.RuntimeVersion() +} + +// Sha3 returns the keccak-256 hash of the passed-in input. +func (a *APIWeb3) Sha3(input string) hexutil.Bytes { + return crypto.Keccak256(hexutil.Bytes(input)) +} diff --git a/eth/rpc/rpcapi/websockets.go b/eth/rpc/rpcapi/websockets.go new file mode 100644 index 000000000..530dd39d7 --- /dev/null +++ b/eth/rpc/rpcapi/websockets.go @@ -0,0 +1,703 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpcapi + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "math/big" + "net" + "net/http" + "strconv" + "sync" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/gorilla/mux" + "github.com/gorilla/websocket" + "github.com/pkg/errors" + + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/params" + gethrpc "github.com/ethereum/go-ethereum/rpc" + + "github.com/cometbft/cometbft/libs/log" + rpcclient "github.com/cometbft/cometbft/rpc/jsonrpc/client" + tmtypes "github.com/cometbft/cometbft/types" + + "github.com/NibiruChain/collections" + + "github.com/NibiruChain/nibiru/v2/app/server/config" + "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/eth/rpc/pubsub" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +type WebsocketsServer interface { + Start() +} + +type SubscriptionResponseJSON struct { + Jsonrpc string `json:"jsonrpc"` + Result any `json:"result"` + ID float64 `json:"id"` +} + +type SubscriptionNotification struct { + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params *SubscriptionResult `json:"params"` +} + +type SubscriptionResult struct { + Subscription gethrpc.ID `json:"subscription"` + Result any `json:"result"` +} + +type ErrorResponseJSON struct { + Jsonrpc string `json:"jsonrpc"` + Error *ErrorMessageJSON `json:"error"` + ID *big.Int `json:"id"` +} + +type ErrorMessageJSON struct { + Code *big.Int `json:"code"` + Message string `json:"message"` +} + +type websocketsServer struct { + rpcAddr string // listen address of rest-server + wsAddr string // listen address of ws server + certFile string + keyFile string + api *pubSubAPI + logger log.Logger +} + +func NewWebsocketsServer( + clientCtx client.Context, + logger log.Logger, + tmWSClient *rpcclient.WSClient, + cfg *config.Config, +) WebsocketsServer { + logger = logger.With("api", "websocket-server") + _, port, _ := net.SplitHostPort(cfg.JSONRPC.Address) // #nosec G703 + + return &websocketsServer{ + rpcAddr: "localhost:" + port, // FIXME: this shouldn't be hardcoded to localhost + wsAddr: cfg.JSONRPC.WsAddress, + certFile: cfg.TLS.CertificatePath, + keyFile: cfg.TLS.KeyPath, + api: newPubSubAPI(clientCtx, logger, tmWSClient), + logger: logger, + } +} + +func (s *websocketsServer) Start() { + ws := mux.NewRouter() + ws.Handle("/", s) + + go func() { + var err error + if s.certFile == "" || s.keyFile == "" { + //#nosec G114 -- http functions have no support for timeouts + err = http.ListenAndServe(s.wsAddr, ws) + } else { + //#nosec G114 -- http functions have no support for timeouts + err = http.ListenAndServeTLS(s.wsAddr, s.certFile, s.keyFile, ws) + } + + if err != nil { + if err == http.ErrServerClosed { + return + } + + s.logger.Error("failed to start HTTP server for WS", "error", err.Error()) + } + }() +} + +func (s *websocketsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + upgrader := websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, + } + + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + s.logger.Debug("websocket upgrade failed", "error", err.Error()) + return + } + + s.readLoop(&wsConn{ + mux: new(sync.Mutex), + conn: conn, + }) +} + +func (s *websocketsServer) sendErrResponse(wsConn *wsConn, msg string) { + res := &ErrorResponseJSON{ + Jsonrpc: "2.0", + Error: &ErrorMessageJSON{ + Code: big.NewInt(-32600), + Message: msg, + }, + ID: nil, + } + + _ = wsConn.WriteJSON(res) // #nosec G703 +} + +type wsConn struct { + conn *websocket.Conn + mux *sync.Mutex +} + +func (w *wsConn) WriteJSON(v any) error { + w.mux.Lock() + defer w.mux.Unlock() + + return w.conn.WriteJSON(v) +} + +func (w *wsConn) Close() error { + w.mux.Lock() + defer w.mux.Unlock() + + return w.conn.Close() +} + +func (w *wsConn) ReadMessage() (messageType int, p []byte, err error) { + // not protected by write mutex + + return w.conn.ReadMessage() +} + +func (s *websocketsServer) readLoop(wsConn *wsConn) { + // subscriptions of current connection + subscriptions := make(map[gethrpc.ID]pubsub.UnsubscribeFunc) + defer func() { + // cancel all subscriptions when connection closed + // #nosec G705 + for _, unsubFn := range subscriptions { + unsubFn() + } + }() + + for { + _, mb, err := wsConn.ReadMessage() + if err != nil { + _ = wsConn.Close() // #nosec G703 + s.logger.Error("read message error, breaking read loop", "error", err.Error()) + return + } + + if isBatch(mb) { + if err := s.tcpGetAndSendResponse(wsConn, mb); err != nil { + s.sendErrResponse(wsConn, err.Error()) + } + continue + } + + var msg map[string]any + if err = json.Unmarshal(mb, &msg); err != nil { + s.sendErrResponse(wsConn, err.Error()) + continue + } + + // check if method == eth_subscribe or eth_unsubscribe + method, ok := msg["method"].(string) + if !ok { + // otherwise, call the usual rpc server to respond + if err := s.tcpGetAndSendResponse(wsConn, mb); err != nil { + s.sendErrResponse(wsConn, err.Error()) + } + + continue + } + + var connID float64 + switch id := msg["id"].(type) { + case string: + connID, err = strconv.ParseFloat(id, 64) + case float64: + connID = id + default: + err = fmt.Errorf("unknown type") + } + if err != nil { + s.sendErrResponse( + wsConn, + fmt.Errorf("invalid type for connection ID: %T", msg["id"]).Error(), + ) + continue + } + + switch method { + case "eth_subscribe": + params, ok := s.getParamsAndCheckValid(msg, wsConn) + if !ok { + continue + } + + subID := gethrpc.NewID() + unsubFn, err := s.api.subscribe(wsConn, subID, params) + if err != nil { + s.sendErrResponse(wsConn, err.Error()) + continue + } + subscriptions[subID] = unsubFn + + res := &SubscriptionResponseJSON{ + Jsonrpc: "2.0", + ID: connID, + Result: subID, + } + + if err := wsConn.WriteJSON(res); err != nil { + break + } + case "eth_unsubscribe": + params, ok := s.getParamsAndCheckValid(msg, wsConn) + if !ok { + continue + } + + id, ok := params[0].(string) + if !ok { + s.sendErrResponse(wsConn, "invalid parameters") + continue + } + + subID := gethrpc.ID(id) + unsubFn, ok := subscriptions[subID] + if ok { + delete(subscriptions, subID) + unsubFn() + } + + res := &SubscriptionResponseJSON{ + Jsonrpc: "2.0", + ID: connID, + Result: ok, + } + + if err := wsConn.WriteJSON(res); err != nil { + break + } + default: + // otherwise, call the usual rpc server to respond + if err := s.tcpGetAndSendResponse(wsConn, mb); err != nil { + s.sendErrResponse(wsConn, err.Error()) + } + } + } +} + +// tcpGetAndSendResponse sends error response to client if params is invalid +func (s *websocketsServer) getParamsAndCheckValid(msg map[string]any, wsConn *wsConn) ([]any, bool) { + params, ok := msg["params"].([]any) + if !ok { + s.sendErrResponse(wsConn, "invalid parameters") + return nil, false + } + + if len(params) == 0 { + s.sendErrResponse(wsConn, "empty parameters") + return nil, false + } + + return params, true +} + +// tcpGetAndSendResponse connects to the rest-server over tcp, posts a JSON-RPC request, and sends the response +// to the client over websockets +func (s *websocketsServer) tcpGetAndSendResponse(wsConn *wsConn, mb []byte) error { + req, err := http.NewRequestWithContext(context.Background(), "POST", "http://"+s.rpcAddr, bytes.NewBuffer(mb)) + if err != nil { + return errors.Wrap(err, "Could not build request") + } + + req.Header.Set("Content-Type", "application/json") + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return errors.Wrap(err, "Could not perform request") + } + + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return errors.Wrap(err, "could not read body from response") + } + + var wsSend any + err = json.Unmarshal(body, &wsSend) + if err != nil { + return errors.Wrap(err, "failed to unmarshal rest-server response") + } + + return wsConn.WriteJSON(wsSend) +} + +// pubSubAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec +type pubSubAPI struct { + events *EventSubscriber + logger log.Logger + clientCtx client.Context +} + +// newPubSubAPI creates an instance of the ethereum PubSub API. +func newPubSubAPI(clientCtx client.Context, logger log.Logger, tmWSClient *rpcclient.WSClient) *pubSubAPI { + logger = logger.With("module", "websocket-client") + return &pubSubAPI{ + events: NewEventSubscriber(logger, tmWSClient), + logger: logger, + clientCtx: clientCtx, + } +} + +func (api *pubSubAPI) subscribe(wsConn *wsConn, subID gethrpc.ID, params []any) (pubsub.UnsubscribeFunc, error) { + method, ok := params[0].(string) + if !ok { + return nil, errors.New("invalid parameters") + } + + switch method { + case "newHeads": + // TODO: handle extra params + return api.subscribeNewHeads(wsConn, subID) + case "logs": + if len(params) > 1 { + return api.subscribeLogs(wsConn, subID, params[1]) + } + return api.subscribeLogs(wsConn, subID, nil) + case "newPendingTransactions": + return api.subscribePendingTransactions(wsConn, subID) + case "syncing": + return api.subscribeSyncing(wsConn, subID) + default: + return nil, errors.Errorf("unsupported method %s", method) + } +} + +func (api *pubSubAPI) subscribeNewHeads(wsConn *wsConn, subID gethrpc.ID) (pubsub.UnsubscribeFunc, error) { + sub, unsubFn, err := api.events.SubscribeNewHeads() + if err != nil { + return nil, errors.Wrap(err, "error creating block filter") + } + + // TODO: use events + baseFeeWeiPerGas := big.NewInt(params.InitialBaseFee) + + go func() { + headersCh := sub.EventCh + errCh := sub.Error() + for { + select { + case event, ok := <-headersCh: + if !ok { + return + } + + data, ok := event.Data.(tmtypes.EventDataNewBlockHeader) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", event.Data)) + continue + } + + header := rpc.EthHeaderFromTendermint(data.Header, gethcore.Bloom{}, baseFeeWeiPerGas) + + // write to ws conn + res := &SubscriptionNotification{ + Jsonrpc: "2.0", + Method: "eth_subscription", + Params: &SubscriptionResult{ + Subscription: subID, + Result: header, + }, + } + + err = wsConn.WriteJSON(res) + if err != nil { + api.logger.Error("error writing header, will drop peer", "error", err.Error()) + + try(func() { + if err != websocket.ErrCloseSent { + _ = wsConn.Close() // #nosec G703 + } + }, api.logger, "closing websocket peer sub") + } + case err, ok := <-errCh: + if !ok { + return + } + api.logger.Debug("dropping NewHeads WebSocket subscription", "subscription-id", subID, "error", err.Error()) + } + } + }() + + return unsubFn, nil +} + +func try(fn func(), l log.Logger, desc string) { + defer func() { + if x := recover(); x != nil { + if err, ok := x.(error); ok { + // debug.PrintStack() + l.Debug("panic during "+desc, "error", err.Error()) + return + } + + l.Debug(fmt.Sprintf("panic during %s: %+v", desc, x)) + return + } + }() + + fn() +} + +func (api *pubSubAPI) subscribeLogs(wsConn *wsConn, subID gethrpc.ID, extra any) (pubsub.UnsubscribeFunc, error) { + crit := filters.FilterCriteria{} + + if extra != nil { + params, ok := extra.(map[string]any) + if !ok { + err := errors.New("invalid criteria") + api.logger.Debug("invalid criteria", "type", fmt.Sprintf("%T", extra)) + return nil, err + } + + if params["address"] != nil { + address, isString := params["address"].(string) + addresses, isSlice := params["address"].([]any) + if !isString && !isSlice { + err := errors.New("invalid addresses; must be address or array of addresses") + api.logger.Debug("invalid addresses", "type", fmt.Sprintf("%T", params["address"])) + return nil, err + } + + if ok { + crit.Addresses = []common.Address{common.HexToAddress(address)} + } + + if isSlice { + crit.Addresses = []common.Address{} + for _, addr := range addresses { + address, ok := addr.(string) + if !ok { + err := errors.New("invalid address") + api.logger.Debug("invalid address", "type", fmt.Sprintf("%T", addr)) + return nil, err + } + + crit.Addresses = append(crit.Addresses, common.HexToAddress(address)) + } + } + } + + if params["topics"] != nil { + topics, ok := params["topics"].([]any) + if !ok { + err := errors.Errorf("invalid topics: %s", topics) + api.logger.Error("invalid topics", "type", fmt.Sprintf("%T", topics)) + return nil, err + } + + crit.Topics = make([][]common.Hash, len(topics)) + + addCritTopic := func(topicIdx int, topic any) error { + tstr, ok := topic.(string) + if !ok { + err := errors.Errorf("invalid topic: %s", topic) + api.logger.Error("invalid topic", "type", fmt.Sprintf("%T", topic)) + return err + } + + crit.Topics[topicIdx] = []common.Hash{common.HexToHash(tstr)} + return nil + } + + for topicIdx, subtopics := range topics { + if subtopics == nil { + continue + } + + // in case we don't have list, but a single topic value + if topic, ok := subtopics.(string); ok { + if err := addCritTopic(topicIdx, topic); err != nil { + return nil, err + } + + continue + } + + // in case we actually have a list of subtopics + subtopicsList, ok := subtopics.([]any) + if !ok { + err := errors.New("invalid subtopics") + api.logger.Error("invalid subtopic", "type", fmt.Sprintf("%T", subtopics)) + return nil, err + } + + subtopicsCollect := make([]common.Hash, len(subtopicsList)) + for idx, subtopic := range subtopicsList { + tstr, ok := subtopic.(string) + if !ok { + err := errors.Errorf("invalid subtopic: %s", subtopic) + api.logger.Error("invalid subtopic", "type", fmt.Sprintf("%T", subtopic)) + return nil, err + } + + subtopicsCollect[idx] = common.HexToHash(tstr) + } + + crit.Topics[topicIdx] = subtopicsCollect + } + } + } + + sub, unsubFn, err := api.events.SubscribeLogs(crit) + if err != nil { + api.logger.Error("failed to subscribe logs", "error", err.Error()) + return nil, err + } + + go func() { + ch := sub.EventCh + errCh := sub.Error() + for { + select { + case event, ok := <-ch: + if !ok { + return + } + + dataTx, ok := event.Data.(tmtypes.EventDataTx) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", event.Data)) + continue + } + + txResponse, err := evm.DecodeTxResponse(dataTx.TxResult.Result.Data) + if err != nil { + api.logger.Error("failed to decode tx response", "error", err.Error()) + return + } + + logs := FilterLogs(evm.LogsToEthereum(txResponse.Logs), crit.FromBlock, crit.ToBlock, crit.Addresses, crit.Topics) + if len(logs) == 0 { + continue + } + + for _, ethLog := range logs { + res := &SubscriptionNotification{ + Jsonrpc: "2.0", + Method: "eth_subscription", + Params: &SubscriptionResult{ + Subscription: subID, + Result: ethLog, + }, + } + + err = wsConn.WriteJSON(res) + if err != nil { + try(func() { + if err != websocket.ErrCloseSent { + _ = wsConn.Close() // #nosec G703 + } + }, api.logger, "closing websocket peer sub") + } + } + case err, ok := <-errCh: + if !ok { + return + } + api.logger.Debug("dropping Logs WebSocket subscription", "subscription-id", subID, "error", err.Error()) + } + } + }() + + return unsubFn, nil +} + +func (api *pubSubAPI) subscribePendingTransactions(wsConn *wsConn, subID gethrpc.ID) (pubsub.UnsubscribeFunc, error) { + sub, unsubFn, err := api.events.SubscribePendingTxs() + if err != nil { + return nil, errors.Wrap(err, "error creating block filter: %s") + } + + go func() { + txsCh := sub.EventCh + errCh := sub.Error() + for { + select { + case ev := <-txsCh: + data, ok := ev.Data.(tmtypes.EventDataTx) + if !ok { + api.logger.Debug("event data type mismatch", "type", fmt.Sprintf("%T", ev.Data)) + continue + } + + fmt.Printf("data.Tx: %s\n", collections.HumanizeBytes(data.Tx)) + ethTxs, err := rpc.RawTxToEthTx(api.clientCtx, data.Tx) + if err != nil { + // not ethereum tx + continue + } + + for _, ethTx := range ethTxs { + // write to ws conn + res := &SubscriptionNotification{ + Jsonrpc: "2.0", + Method: "eth_subscription", + Params: &SubscriptionResult{ + Subscription: subID, + Result: ethTx.Hash, + }, + } + + err = wsConn.WriteJSON(res) + if err != nil { + api.logger.Debug("error writing header, will drop peer", "error", err.Error()) + + try(func() { + if err != websocket.ErrCloseSent { + _ = wsConn.Close() // #nosec G703 + } + }, api.logger, "closing websocket peer sub") + } + } + case err, ok := <-errCh: + if !ok { + return + } + api.logger.Debug("dropping PendingTransactions WebSocket subscription", subID, "error", err.Error()) + } + } + }() + + return unsubFn, nil +} + +func (api *pubSubAPI) subscribeSyncing(_ *wsConn, _ gethrpc.ID) (pubsub.UnsubscribeFunc, error) { + return nil, errors.New("syncing subscription is not implemented") +} + +// copy from github.com/ethereum/go-ethereum/rpc/json.go +// isBatch returns true when the first non-whitespace characters is '[' +func isBatch(raw []byte) bool { + for _, c := range raw { + // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) + if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { + continue + } + return c == '[' + } + return false +} diff --git a/eth/rpc/types.go b/eth/rpc/types.go new file mode 100644 index 000000000..dfcec77ab --- /dev/null +++ b/eth/rpc/types.go @@ -0,0 +1,92 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package rpc + +// TODO: docs(eth-rpc): Explain types further. + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" +) + +// Copied the Account and StorageResult types since they are registered under an +// internal pkg on geth. + +// AccountResult struct for account proof +type AccountResult struct { + Address common.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *hexutil.Big `json:"balance"` + CodeHash common.Hash `json:"codeHash"` + Nonce hexutil.Uint64 `json:"nonce"` + StorageHash common.Hash `json:"storageHash"` + StorageProof []StorageResult `json:"storageProof"` +} + +// StorageResult defines the format for storage proof return +type StorageResult struct { + Key string `json:"key"` + Value *hexutil.Big `json:"value"` + Proof []string `json:"proof"` +} + +// EthTxJsonRPC represents a transaction that will serialize to the RPC representation of a transaction +type EthTxJsonRPC struct { + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` + GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + Type hexutil.Uint64 `json:"type"` + Accesses *gethcore.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` +} + +// StateOverride is the collection of overridden accounts. +type StateOverride map[common.Address]OverrideAccount + +// OverrideAccount indicates the overriding fields of account during the execution of +// a message call. +// Note, state and stateDiff can't be specified at the same time. If state is +// set, message execution will only use the data in the given state. Otherwise +// if statDiff is set, all diff will be applied first and then execute the call +// message. +type OverrideAccount struct { + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance **hexutil.Big `json:"balance"` + State *map[common.Hash]common.Hash `json:"state"` + StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` +} + +type FeeHistoryResult struct { + OldestBlock *hexutil.Big `json:"oldestBlock"` + Reward [][]*hexutil.Big `json:"reward,omitempty"` + BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` + GasUsedRatio []float64 `json:"gasUsedRatio"` +} + +// SignTransactionResult represents a RLP encoded signed transaction. +type SignTransactionResult struct { + Raw hexutil.Bytes `json:"raw"` + Tx *gethcore.Transaction `json:"tx"` +} + +type OneFeeHistory struct { + BaseFee, NextBaseFee *big.Int // base fee for each block + Reward []*big.Int // each element of the array will have the tip provided to miners for the percentile given + GasUsedRatio float64 // the ratio of gas used to the gas limit for each block +} diff --git a/eth/safe_math.go b/eth/safe_math.go new file mode 100644 index 000000000..d1ca9544d --- /dev/null +++ b/eth/safe_math.go @@ -0,0 +1,37 @@ +package eth + +import ( + fmt "fmt" + math "math" + "math/big" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" +) + +const maxBitLen = 256 + +// SafeNewIntFromBigInt constructs Int from big.Int, return error if more than 256bits +func SafeNewIntFromBigInt(i *big.Int) (sdkmath.Int, error) { + if !IsValidInt256(i) { + return sdkmath.NewInt(0), fmt.Errorf("big int out of bound: %s", i) + } else if i == nil { + return sdkmath.Int{}, fmt.Errorf("received nil pointer for *big.Int") + } + return sdkmath.NewIntFromBigInt(i), nil +} + +// IsValidInt256 check the bound of 256 bit number +func IsValidInt256(i *big.Int) bool { + return i == nil || i.BitLen() <= maxBitLen +} + +// SafeInt64 checks for overflows while casting a uint64 to int64 value. +func SafeInt64(value uint64) (int64, error) { + if value > uint64(math.MaxInt64) { + return 0, errorsmod.Wrapf(errortypes.ErrInvalidHeight, "uint64 value %v cannot exceed %v", value, int64(math.MaxInt64)) + } + + return int64(value), nil // #nosec G701 -- checked for int overflow already +} diff --git a/eth/safe_math_test.go b/eth/safe_math_test.go new file mode 100644 index 000000000..9be278637 --- /dev/null +++ b/eth/safe_math_test.go @@ -0,0 +1,120 @@ +package eth_test + +import ( + fmt "fmt" + "math/big" + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +const maxInt64 = 9223372036854775807 + +type Suite struct { + suite.Suite +} + +func TestSuite_RunAll(t *testing.T) { + suite.Run(t, new(Suite)) +} + +func (s *Suite) TestSafeNewIntFromBigInt() { + tests := []struct { + name string + input *big.Int + expectErr bool + }{ + { + name: "Valid input within 256-bit limit", + input: big.NewInt(maxInt64), // Use max int64 as a valid test case + expectErr: false, + }, + { + name: "Invalid input exceeding 256-bit limit", + input: new(big.Int).Lsh(big.NewInt(1), 257), // Shift 1 left by 257 places, creating a 258-bit number + expectErr: true, + }, + { + name: "Nil input", + input: nil, + expectErr: true, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + result, err := eth.SafeNewIntFromBigInt(tc.input) + if tc.expectErr { + s.Error(err, fmt.Sprintf("result: %s", result)) + } else { + s.NoError(err, fmt.Sprintf("result: %s", result)) + s.Equal(math.NewIntFromBigInt(tc.input), result, "The results should be equal") + } + }) + } +} + +func (s *Suite) TestIsValidInt256() { + tests := []struct { + name string + input *big.Int + expectValid bool + }{ + { + name: "Valid 256-bit number", + input: new(big.Int).Lsh(big.NewInt(1), 255), // Exactly 256-bit number + expectValid: true, + }, + { + name: "Invalid 257-bit number", + input: new(big.Int).Lsh(big.NewInt(1), 256), // 257-bit number + expectValid: false, + }, + { + name: "Nil input", + input: nil, + expectValid: true, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + result := eth.IsValidInt256(tc.input) + s.Equal(tc.expectValid, result, "Validity check did not match expected") + }) + } +} + +func (s *Suite) TestSafeInt64() { + tests := []struct { + name string + input uint64 + expectErr bool + }{ + { + name: "Valid conversion", + input: maxInt64, // Maximum value for int64 + expectErr: false, + }, + { + name: "Invalid conversion causes overflow", + input: uint64(maxInt64) + 1, // Exceeds maximum int64 value + expectErr: true, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + result, err := eth.SafeInt64(tc.input) + if tc.expectErr { + s.Error(err, "Expected an error due to overflow but did not get one") + } else { + s.NoError(err, "Did not expect an error but got one") + s.Equal(int64(tc.input), result, "The results should match") + } + }) + } +} diff --git a/eth/state_encoder.go b/eth/state_encoder.go new file mode 100644 index 000000000..bb3fbaef6 --- /dev/null +++ b/eth/state_encoder.go @@ -0,0 +1,73 @@ +package eth + +import ( + fmt "fmt" + + "github.com/NibiruChain/collections" + gethcommon "github.com/ethereum/go-ethereum/common" +) + +// BytesToHex converts a byte array to a hexadecimal string +func BytesToHex(bz []byte) string { + return fmt.Sprintf("%x", bz) +} + +var ( + // Implements a `collections.ValueEncoder` for the `[]byte` type + ValueEncoderBytes collections.ValueEncoder[[]byte] = veBytes{} + KeyEncoderBytes collections.KeyEncoder[[]byte] = keBytes{} + + // Implements a `collections.ValueEncoder` for an Ethereum address. + ValueEncoderEthAddr collections.ValueEncoder[gethcommon.Address] = veEthAddr{} + // keEthHash: Implements a `collections.KeyEncoder` for an Ethereum address. + KeyEncoderEthAddr collections.KeyEncoder[gethcommon.Address] = keEthAddr{} + + // keEthHash: Implements a `collections.KeyEncoder` for an Ethereum hash. + KeyEncoderEthHash collections.KeyEncoder[gethcommon.Hash] = keEthHash{} +) + +// collections ValueEncoder[[]byte] +type veBytes struct{} + +func (_ veBytes) Encode(value []byte) []byte { return value } +func (_ veBytes) Decode(bz []byte) []byte { return bz } +func (_ veBytes) Stringify(value []byte) string { return BytesToHex(value) } +func (_ veBytes) Name() string { return "[]byte" } + +// veEthAddr: Implements a `collections.ValueEncoder` for an Ethereum address. +type veEthAddr struct{} + +func (_ veEthAddr) Encode(value gethcommon.Address) []byte { return value.Bytes() } +func (_ veEthAddr) Decode(bz []byte) gethcommon.Address { return gethcommon.BytesToAddress(bz) } +func (_ veEthAddr) Stringify(value gethcommon.Address) string { return value.Hex() } +func (_ veEthAddr) Name() string { return "gethcommon.Address" } + +type keBytes struct{} + +// Encode encodes the type T into bytes. +func (_ keBytes) Encode(key []byte) []byte { return key } + +// Decode decodes the given bytes back into T. +// And it also must return the bytes of the buffer which were read. +func (_ keBytes) Decode(bz []byte) (int, []byte) { return len(bz), bz } + +// Stringify returns a string representation of T. +func (_ keBytes) Stringify(key []byte) string { return BytesToHex(key) } + +// keEthAddr: Implements a `collections.KeyEncoder` for an Ethereum address. +type keEthAddr struct{} + +func (_ keEthAddr) Encode(value gethcommon.Address) []byte { return value.Bytes() } +func (_ keEthAddr) Decode(bz []byte) (int, gethcommon.Address) { + return gethcommon.AddressLength, gethcommon.BytesToAddress(bz) +} +func (_ keEthAddr) Stringify(value gethcommon.Address) string { return value.Hex() } + +// keEthHash: Implements a `collections.KeyEncoder` for an Ethereum hash. +type keEthHash struct{} + +func (_ keEthHash) Encode(value gethcommon.Hash) []byte { return value.Bytes() } +func (_ keEthHash) Decode(bz []byte) (int, gethcommon.Hash) { + return gethcommon.HashLength, gethcommon.BytesToHash(bz) +} +func (_ keEthHash) Stringify(value gethcommon.Hash) string { return value.Hex() } diff --git a/eth/state_encoder_test.go b/eth/state_encoder_test.go new file mode 100644 index 000000000..a732d4027 --- /dev/null +++ b/eth/state_encoder_test.go @@ -0,0 +1,88 @@ +package eth_test + +import ( + "testing" + + "github.com/NibiruChain/collections" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +func assertBijectiveKey[T any](t *testing.T, encoder collections.KeyEncoder[T], key T) { + encodedKey := encoder.Encode(key) + readLen, decodedKey := encoder.Decode(encodedKey) + require.Equal(t, len(encodedKey), readLen, "encoded key and read bytes must have same size") + require.Equal(t, key, decodedKey, "encoding and decoding produces different keys") + wantStr := encoder.Stringify(key) + gotStr := encoder.Stringify(decodedKey) + require.Equal(t, wantStr, gotStr, + "encoding and decoding produce different string representations") +} + +func assertBijectiveValue[T any](t *testing.T, encoder collections.ValueEncoder[T], value T) { + encodedValue := encoder.Encode(value) + decodedValue := encoder.Decode(encodedValue) + require.Equal(t, value, decodedValue, "encoding and decoding produces different values") + + wantStr := encoder.Stringify(value) + gotStr := encoder.Stringify(decodedValue) + require.Equal(t, wantStr, gotStr, + "encoding and decoding produce different string representations") + require.NotEmpty(t, encoder.Name()) +} + +func (s *Suite) TestEncoderBytes() { + testCases := []struct { + name string + value string + }{ + {"dec-like number", "-1000.5858"}, + {"Nibiru bech32 addr", "nibi1rlvdjfmxkyfj4tzu73p8m4g2h4y89xccf9622l"}, + {"Nibiru EVM addr", "0xA52c829E935C30F4C7dcD66739Cf91BF79dD9253"}, + {"normal text with special symbols", "abc123日本語!!??foobar"}, + } + for _, tc := range testCases { + s.Run("bijectivity: []byte encoders "+tc.name, func() { + given := []byte(tc.value) + assertBijectiveKey(s.T(), eth.KeyEncoderBytes, given) + assertBijectiveValue(s.T(), eth.ValueEncoderBytes, given) + }) + } +} + +func (s *Suite) TestEncoderEthAddr() { + testCases := []struct { + name string + given gethcommon.Address + wantPanic bool + }{ + { + name: "Nibiru EVM addr", + given: gethcommon.BytesToAddress([]byte("0xA52c829E935C30F4C7dcD66739Cf91BF79dD9253")), + }, + { + name: "Nibiru EVM addr length above 20 bytes", + given: gethcommon.BytesToAddress([]byte("0xA52c829E935C30F4C7dcD66739Cf91BF79dD92532456BF123421")), + }, + { + name: "Nibiru Bech 32 addr (hypothetically)", + given: gethcommon.Address([]byte("nibi1rlvdjfmxkyfj4tzu73p8m4g2h4y89xccf9622l")), + }, + } + for _, tc := range testCases { + s.Run("bijectivity: []byte encoders "+tc.name, func() { + given := tc.given + runTest := func() { + assertBijectiveKey(s.T(), eth.KeyEncoderEthAddr, given) + assertBijectiveValue(s.T(), eth.ValueEncoderEthAddr, given) + } + if tc.wantPanic { + s.Require().Panics(runTest) + } else { + s.Require().NotPanics(runTest) + } + }) + } +} diff --git a/eth/stringify.go b/eth/stringify.go new file mode 100644 index 000000000..2a2775213 --- /dev/null +++ b/eth/stringify.go @@ -0,0 +1,35 @@ +package eth + +import ( + "encoding/hex" + fmt "fmt" + + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" +) + +// TmTxHashToString returns the consensus transaction hash as a string. +// Transactions are hex-encoded and capitlized. +// Reference: Tx.String function from comet-bft/types/tx.go +func TmTxHashToString(tmTxHash []byte) string { + return fmt.Sprintf("%X", tmTxHash) +} + +// EthTxHashToString returns the EVM transaction hash as a string. +func EthTxHashToString(hash gethcommon.Hash) string { + return hash.Hex() +} + +// BloomToHex returns the bloom filter as a string. +func BloomToHex(bloom gethcore.Bloom) string { + return BytesToHex(bloom.Bytes()) +} + +// BloomFromHex converts a hex-encoded bloom filter to a gethcore.Bloom. +func BloomFromHex(bloomHex string) (gethcore.Bloom, error) { + bloomBz, err := hex.DecodeString(bloomHex) + if err != nil { + return gethcore.Bloom{}, fmt.Errorf("could not construct bloom: %w", err) + } + return gethcore.BytesToBloom(bloomBz), nil +} diff --git a/eth/stringify_test.go b/eth/stringify_test.go new file mode 100644 index 000000000..4e826126b --- /dev/null +++ b/eth/stringify_test.go @@ -0,0 +1,23 @@ +package eth_test + +import ( + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +func (s *Suite) TestStringify() { + testCases := []gethcore.Bloom{ + gethcore.BytesToBloom([]byte("alphanumeric123")), + gethcore.BytesToBloom([]byte{}), + gethcore.BytesToBloom(gethcommon.Big0.Bytes()), + gethcore.BytesToBloom(gethcommon.Big1.Bytes()), + } + for tcIdx, bloom := range testCases { + gotStr := eth.BloomToHex(bloom) + gotBloom, err := eth.BloomFromHex(gotStr) + s.NoError(err) + s.Equalf(bloom, gotBloom, "test case: %d", tcIdx) + } +} diff --git a/evm-e2e/.env_sample b/evm-e2e/.env_sample new file mode 100644 index 000000000..f0492b948 --- /dev/null +++ b/evm-e2e/.env_sample @@ -0,0 +1,4 @@ +JSON_RPC_ENDPOINT="http://127.0.0.1:8545" +MNEMONIC="guard cream sadness conduct invite crumble clock pudding hole grit liar hotel maid produce squeeze return argue turtle know drive eight casino maze host" +TEST_TIMEOUT=15000 +TX_WAIT_TIMEOUT=5000 diff --git a/evm-e2e/.gitignore b/evm-e2e/.gitignore new file mode 100644 index 000000000..3419dc450 --- /dev/null +++ b/evm-e2e/.gitignore @@ -0,0 +1,4 @@ +types +artifacts +cache +.env diff --git a/evm-e2e/.nvmrc b/evm-e2e/.nvmrc new file mode 100644 index 000000000..3c032078a --- /dev/null +++ b/evm-e2e/.nvmrc @@ -0,0 +1 @@ +18 diff --git a/evm-e2e/.prettierrc b/evm-e2e/.prettierrc new file mode 100644 index 000000000..0d3fe8121 --- /dev/null +++ b/evm-e2e/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 120, + "tabWidth": 2, + "importOrder": ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true +} diff --git a/evm-e2e/README.md b/evm-e2e/README.md new file mode 100644 index 000000000..508956d15 --- /dev/null +++ b/evm-e2e/README.md @@ -0,0 +1,60 @@ +# EVM Tests + +Folder contains ethers.js test bundle which executes main +Nibiru EVM methods via JSON RPC. + +Contract [TestERC20.sol](./contracts/TestERC20.sol) represents +simple ERC20 token with initial supply `1000,000 * 10e18` tokens. + +Contract is compiled via HardHat into [json file](./contracts/TestERC20Compiled.json) +with ABI and bytecode. + +## Setup and Run + +### Run Nibiru node + +Tests require Nibiru node running with JSON RPC enabled. + +Localnet has JSON RPC enabled by default. + +### Install dependencies + +```bash +npm install +``` + +### Configure environment in `.env` file + +Use [env.sample](./.env_sample) as a reference. + +```ini +JSON_RPC_ENDPOINT="http://127.0.0.1:8545" +MNEMONIC="guard cream sadness conduct invite crumble clock pudding hole grit liar hotel maid produce squeeze return argue turtle know drive eight casino maze host" +``` + +### Execute + +```bash +❯ bun test +bun test v1.1.12 (43f0913c) + +test/erc20.test.ts: +✓ ERC-20 contract tests > should send properly [8410.41ms] + +test/native_transfer.test.ts: +✓ Basic Queries > Simple transfer, balance check [4251.02ms] + +test/contract_infinite_loop_gas.test.ts: +✓ Infinite loop gas contract > should fail due to out of gas error [8281.13ms] + +test/contract_send_nibi.test.ts: +✓ Send NIBI via smart contract > should send via transfer method [4228.80ms] +✓ Send NIBI via smart contract > should send via send method [4231.87ms] +✓ Send NIBI via smart contract > should send via transfer method [4213.43ms] + + 6 pass + 0 fail + 22 expect() calls +Ran 6 tests across 4 files. [38.08s] + +``` diff --git a/evm-e2e/bun.lockb b/evm-e2e/bun.lockb new file mode 100755 index 000000000..b88cb597f Binary files /dev/null and b/evm-e2e/bun.lockb differ diff --git a/evm-e2e/contracts/EventsEmitter.sol b/evm-e2e/contracts/EventsEmitter.sol new file mode 100644 index 000000000..e9bd2920b --- /dev/null +++ b/evm-e2e/contracts/EventsEmitter.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +contract EventsEmitter { + event TestEvent(address indexed sender, uint256 value); + + function emitEvent(uint256 value) public { + emit TestEvent(msg.sender, value); + } +} \ No newline at end of file diff --git a/evm-e2e/contracts/InfiniteLoopGas.sol b/evm-e2e/contracts/InfiniteLoopGas.sol new file mode 100644 index 000000000..beae47115 --- /dev/null +++ b/evm-e2e/contracts/InfiniteLoopGas.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +contract InifiniteLoopGas { + uint256 public counter = 0; + + // Using up all of the gas that you send causes your transaction to fail. + // State changes are undone. + // Gas spent are not refunded. + function forever() public { + // Here we run a loop until all of the gas are spent + // and the transaction fails + while (true) { + counter += 1; + } + } +} diff --git a/evm-e2e/contracts/NibiruOracleChainLinkLike.sol b/evm-e2e/contracts/NibiruOracleChainLinkLike.sol new file mode 100644 index 000000000..f81b38837 --- /dev/null +++ b/evm-e2e/contracts/NibiruOracleChainLinkLike.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import '@nibiruchain/solidity/contracts/IOracle.sol'; + +/// @title NibiruOracleChainLinkLike +/// @notice This contract serves as a ChainLink-like data feed that sources its +/// "answer" value from the Nibiru Oracle system. The Nibiru Oracle gives price +/// data with 18 decimals universally, and that 18-decimal answer is scaled to +/// have the number of decimals specified by "decimals()". This is set at the +/// time of deployment. +/// _ _ _____ ____ _____ _____ _ _ +/// | \ | ||_ _|| _ \|_ _|| __ \ | | | | +/// | \| | | | | |_) | | | | |__) || | | | +/// | . ` | | | | _ < | | | _ / | | | | +/// | |\ | _| |_ | |_) |_| |_ | | \ \ | |__| | +/// |_| \_||_____||____/|_____||_| \_\ \____/ +/// +contract NibiruOracleChainLinkLike is ChainLinkAggregatorV3Interface { + string public pair; + uint8 public _decimals; + + constructor(string memory _pair, uint8 _dec) { + require(_dec <= 18, 'Decimals cannot exceed 18'); + require(bytes(_pair).length > 0, 'Pair string cannot be empty'); + pair = _pair; + _decimals = _dec; + } + + function decimals() external view override returns (uint8) { + return _decimals; + } + + /// @notice Returns a human-readable description of the oracle and its data + /// feed identifier (pair) in the Nibiru Oracle system + function description() external view override returns (string memory) { + return string.concat('Nibiru Oracle ChainLink-like price feed for ', pair); + } + + /// @notice Oracle version number. Hardcoded to 1. + function version() external pure override returns (uint256) { + return 1; + } + + /// @notice Returns the latest data from the Nibiru Oracle. + /// @return roundId The block number when the answer was published onchain. + /// @return answer Data feed result scaled to the precision specified by + /// "decimals()" + /// @return startedAt UNIX timestamp in seconds when "answer" was published. + /// @return updatedAt UNIX timestamp in seconds when "answer" was published. + /// @return answeredInRound The ID of the round where the answer was computed. + /// Since the Nibiru Oracle does not have ChainLink's system of voting + /// rounds, this argument is a meaningless, arbitrary constant. + function latestRoundData() + public + view + override + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + ( + uint80 _roundId, + int256 answer18Dec, + uint256 _startedAt, + uint256 _updatedAt, + uint80 _answeredInRound + ) = NIBIRU_ORACLE.chainLinkLatestRoundData(pair); + answer = scaleAnswerToDecimals(answer18Dec); + return (_roundId, answer, _startedAt, _updatedAt, _answeredInRound); + } + + /// @notice Returns the latest data from the Nibiru Oracle. Historical round + /// retrieval is not supported. This method is a duplicate of + /// "latestRoundData". + /// @return roundId The block number when the answer was published onchain. + /// @return answer Data feed result scaled to the precision specified by + /// "decimals()" + /// @return startedAt UNIX timestamp in seconds when "answer" was published. + /// @return updatedAt UNIX timestamp in seconds when "answer" was published. + /// @return answeredInRound The ID of the round where the answer was computed. + /// Since the Nibiru Oracle does not have ChainLink's system of voting + /// rounds, this argument is a meaningless, arbitrary constant. + function getRoundData( + uint80 + ) + external + view + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + return latestRoundData(); + } + + function scaleAnswerToDecimals(int256 answer18Dec) internal view returns (int256 answer) { + // Default answers are in 18 decimals. + // Scale down to the decimals specified in the constructor. + uint8 pow10 = 18 - _decimals; + return answer18Dec / int256(10 ** pow10); + } +} diff --git a/evm-e2e/contracts/SendReceiveNibi.sol b/evm-e2e/contracts/SendReceiveNibi.sol new file mode 100644 index 000000000..1be8479f0 --- /dev/null +++ b/evm-e2e/contracts/SendReceiveNibi.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +contract ReceiveNibi { + /* + Which function is called, fallback() or receive()? + + send Nibi + | + msg.data is empty? + / \ + yes no + / \ + receive() exists? fallback() + / \ + yes no + / \ + receive() fallback() + */ + + // Function to receive Nibi. msg.data must be empty + receive() external payable {} + + // Fallback function is called when msg.data is not empty + fallback() external payable {} + + function getBalance() public view returns (uint256) { + return address(this).balance; + } +} + +contract SendNibi { + function sendViaTransfer(address payable _to) public payable { + // This function is no longer recommended for sending Nibi. + _to.transfer(msg.value); + } + + function sendViaSend(address payable _to) public payable { + // Send returns a boolean value indicating success or failure. + // This function is not recommended for sending Nibi. + bool sent = _to.send(msg.value); + require(sent, "Failed to send Nibi"); + } + + function sendViaCall(address payable _to) public payable { + // Call returns a boolean value indicating success or failure. + // This is the current recommended method to use. + (bool sent, bytes memory data) = _to.call{value : msg.value}(""); + require(sent, "Failed to send Nibi"); + } +} diff --git a/evm-e2e/contracts/TestERC20.sol b/evm-e2e/contracts/TestERC20.sol new file mode 100644 index 000000000..bea532843 --- /dev/null +++ b/evm-e2e/contracts/TestERC20.sol @@ -0,0 +1,16 @@ +// contracts/TestERC20.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestERC20 is ERC20 { + + // Define the supply of TestERC20: 1,000,000 + uint256 constant initialSupply = 1000000 * (10**18); + + // Constructor will be called on contract creation + constructor() ERC20("TestERC20", "FOO") { + _mint(msg.sender, initialSupply); + } +} diff --git a/evm-e2e/contracts/TransactionReverter.sol b/evm-e2e/contracts/TransactionReverter.sol new file mode 100644 index 000000000..81d7c0949 --- /dev/null +++ b/evm-e2e/contracts/TransactionReverter.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract TransactionReverter { + uint256 public value; + + error CustomRevertReason(uint256 providedValue); + + // Will try to set state and revert unconditionally + function setAndRevert(uint256 newValue) public { + value = newValue; + revert("Transaction reverted after state change"); + } + + // Will revert with custom error + function revertWithCustomError(uint256 newValue) public { + value = newValue; + revert CustomRevertReason(newValue); + } + + // Will emit event and then revert + event ValueUpdateAttempted(address sender, uint256 value); + function emitAndRevert(uint256 newValue) public { + emit ValueUpdateAttempted(msg.sender, newValue); + value = newValue; + revert("Reverted after event emission"); + } +} \ No newline at end of file diff --git a/evm-e2e/hardhat.config.js b/evm-e2e/hardhat.config.js new file mode 100644 index 000000000..898700747 --- /dev/null +++ b/evm-e2e/hardhat.config.js @@ -0,0 +1,13 @@ +require("@nomicfoundation/hardhat-toolbox"); +require('@typechain/hardhat'); + +/** @type import('hardhat/config').HardhatUserConfig */ +module.exports = { + solidity: "0.8.24", + typechain: { + outDir: "types", + target: "ethers-v6", + alwaysGenerateOverloads: false, + dontOverrideCompile: false, + }, +}; diff --git a/evm-e2e/jest.config.js b/evm-e2e/jest.config.js new file mode 100644 index 000000000..dcd46bd5f --- /dev/null +++ b/evm-e2e/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + testEnvironment: 'node', + transform: { + '^.+\\.(ts|tsx)?$': 'ts-jest', + }, + testMatch: ['**/test/**/*.test.ts'], + verbose: true, + "maxWorkers": 1 +}; diff --git a/evm-e2e/justfile b/evm-e2e/justfile new file mode 100644 index 000000000..396488f58 --- /dev/null +++ b/evm-e2e/justfile @@ -0,0 +1,28 @@ +# Use this justfile by +# (1) installing with "cargo install just" +# (2) running the "just" command. + +# Displays available recipes by running `just -l`. +setup: + #!/usr/bin/env bash + just -l + +# Install all dependencies +install: + npm install + npx hardhat typechain + +# Runs the E2E tests +test: + npm test + +# Runs tx receipt tests. Used for testnet quick check. +test-basic: + npm test -- tx_receipt.test.ts + +# Format +fmt: + npm run format + +gen-types: + npx hardhat typechain diff --git a/evm-e2e/package-lock.json b/evm-e2e/package-lock.json new file mode 100644 index 000000000..2f0808745 --- /dev/null +++ b/evm-e2e/package-lock.json @@ -0,0 +1,13339 @@ +{ + "name": "nibiru-evm-test", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nibiru-evm-test", + "version": "0.0.1", + "license": "ISC", + "devDependencies": { + "@jest/globals": "^29.7.0", + "@nibiruchain/solidity": "^0.0.2", + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@openzeppelin/contracts": "^5.1.0", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", + "@types/jest": "^29.5.12", + "dotenv": "^16.4.5", + "eslint": "^8.0.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-prettier": "^5.1.3", + "ethers": "^6.12.1", + "hardhat": "^2.22.15", + "jest": "^29.7.0", + "prettier": "^3.3.3", + "ts-jest": "^29.2.4", + "typechain": "^8.3.2" + }, + "engines": { + "node": ">=0.18.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.2" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", + "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT" + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@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.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@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": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@nibiruchain/solidity": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@nibiruchain/solidity/-/solidity-0.0.2.tgz", + "integrity": "sha512-ZWL4J5bdlzV6tQ8GemdKqWnFjxgvXCG5rjhsXzuh3rFDmdGNal9GOsCO2B2wFnPEctfg+XOOKZpLF8278/xiJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@openzeppelin/contracts": "^4.9.0" + } + }, + "node_modules/@nibiruchain/solidity/node_modules/@openzeppelin/contracts": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.6.tgz", + "integrity": "sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nomicfoundation/edr": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.6.4.tgz", + "integrity": "sha512-YgrSuT3yo5ZQkbvBGqQ7hG+RDvz3YygSkddg4tb1Z0Y6pLXFzwrcEwWaJCFAVeeZxdxGfCgGMUYgRVneK+WXkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.6.4", + "@nomicfoundation/edr-darwin-x64": "0.6.4", + "@nomicfoundation/edr-linux-arm64-gnu": "0.6.4", + "@nomicfoundation/edr-linux-arm64-musl": "0.6.4", + "@nomicfoundation/edr-linux-x64-gnu": "0.6.4", + "@nomicfoundation/edr-linux-x64-musl": "0.6.4", + "@nomicfoundation/edr-win32-x64-msvc": "0.6.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.6.4.tgz", + "integrity": "sha512-QNQErISLgssV9+qia8sIjRANqtbW8snSDvjspixT/kSQ5ZSGxxctTg7x72wPSrcu8+EBEveIe5uqENIp5GH8HQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.6.4.tgz", + "integrity": "sha512-cjVmREiwByyc9+oGfvAh49IAw+oVJHF9WWYRD+Tm/ZlSpnEVWxrGNBak2bd/JSYjn+mZE7gmWS4SMRi4nKaLUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.6.4.tgz", + "integrity": "sha512-96o9kRIVD6W5VkgKvUOGpWyUGInVQ5BRlME2Fa36YoNsRQMaKtmYJEU0ACosYES6ZTpYC8U5sjMulvPtVoEfOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.6.4.tgz", + "integrity": "sha512-+JVEW9e5plHrUfQlSgkEj/UONrIU6rADTEk+Yp9pbe+mzNkJdfJYhs5JYiLQRP4OjxH4QOrXI97bKU6FcEbt5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.6.4.tgz", + "integrity": "sha512-nzYWW+fO3EZItOeP4CrdMgDXfaGBIBkKg0Y/7ySpUxLqzut40O4Mb0/+quqLAFkacUSWMlFp8nsmypJfOH5zoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.6.4.tgz", + "integrity": "sha512-QFRoE9qSQ2boRrVeQ1HdzU+XN7NUgwZ1SIy5DQt4d7jCP+5qTNsq8LBNcqhRBOATgO63nsweNUhxX/Suj5r1Sw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.6.4.tgz", + "integrity": "sha512-2yopjelNkkCvIjUgBGhrn153IBPLwnsDeNiq6oA0WkeM8tGmQi4td+PGi9jAriUDAkc59Yoi2q9hYA6efiY7Zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/ethereumjs-util": "9.0.4" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", + "dev": true, + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/hardhat-chai-matchers": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.8.tgz", + "integrity": "sha512-Z5PiCXH4xhNLASROlSUOADfhfpfhYO6D7Hn9xp8PddmHey0jq704cr6kfU8TRrQ4PUZbpfsZadPj+pCfZdjPIg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai-as-promised": "^7.1.3", + "chai-as-promised": "^7.1.1", + "deep-eql": "^4.0.1", + "ordinal": "^1.0.3" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "chai": "^4.2.0", + "ethers": "^6.1.0", + "hardhat": "^2.9.4" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.8.tgz", + "integrity": "sha512-zhOZ4hdRORls31DTOqg+GmEZM0ujly8GGIuRY7t7szEk2zW/arY1qDug/py8AEktT00v5K+b6RvbVog+va51IA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "lodash.isequal": "^4.5.0" + }, + "peerDependencies": { + "ethers": "^6.1.0", + "hardhat": "^2.0.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition": { + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-0.15.7.tgz", + "integrity": "sha512-RFhGazR0/JqHxuuIxjjMmM+nWFqEvA7wcVqcX7vUqqmAIGuok4HhnWQH8aOvBaVguiXvvlFDJL0PIlxmkFgIUg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nomicfoundation/ignition-core": "^0.15.7", + "@nomicfoundation/ignition-ui": "^0.15.7", + "chalk": "^4.0.0", + "debug": "^4.3.2", + "fs-extra": "^10.0.0", + "json5": "^2.2.3", + "prompts": "^2.4.2" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-verify": "^2.0.1", + "hardhat": "^2.18.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition-ethers": { + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-0.15.7.tgz", + "integrity": "sha512-pUZWQeFNMwDe6F/yKIJsCo+87elk/M/Edjp6AnWWIBplRyPa13Nh63+yOqMSSd9Mx9lLuBaEGnYXoI2Uz2wYZA==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.4", + "@nomicfoundation/hardhat-ignition": "^0.15.7", + "@nomicfoundation/ignition-core": "^0.15.7", + "ethers": "^6.7.0", + "hardhat": "^2.18.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@nomicfoundation/hardhat-network-helpers": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.12.tgz", + "integrity": "sha512-xTNQNI/9xkHvjmCJnJOTyqDSl8uq1rKb2WOVmixQxFtRd7Oa3ecO8zM0cyC2YmOK+jHB9WPZ+F/ijkHg1CoORA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ethereumjs-util": "^7.1.4" + }, + "peerDependencies": { + "hardhat": "^2.9.5" + } + }, + "node_modules/@nomicfoundation/hardhat-network-helpers/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/hardhat-network-helpers/node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@nomicfoundation/hardhat-toolbox": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-5.0.0.tgz", + "integrity": "sha512-FnUtUC5PsakCbwiVNsqlXVIWG5JIb5CEZoSXbJUsEBun22Bivx2jhF1/q9iQbzuaGpJKFQyOhemPB2+XlEE6pQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0", + "@nomicfoundation/hardhat-network-helpers": "^1.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "@typechain/ethers-v6": "^0.5.0", + "@typechain/hardhat": "^9.0.0", + "@types/chai": "^4.2.0", + "@types/mocha": ">=9.1.0", + "@types/node": ">=18.0.0", + "chai": "^4.2.0", + "ethers": "^6.4.0", + "hardhat": "^2.11.0", + "hardhat-gas-reporter": "^1.0.8", + "solidity-coverage": "^0.8.1", + "ts-node": ">=8.0.0", + "typechain": "^8.3.0", + "typescript": ">=4.5.0" + } + }, + "node_modules/@nomicfoundation/hardhat-verify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.11.tgz", + "integrity": "sha512-lGIo4dNjVQFdsiEgZp3KP6ntLiF7xJEJsbNHfSyIiFCyI0Yv0518ElsFtMC5uCuHEChiBBMrib9jWQvHHT+X3Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@ethersproject/address": "^5.0.2", + "cbor": "^8.1.0", + "chalk": "^2.4.2", + "debug": "^4.1.1", + "lodash.clonedeep": "^4.5.0", + "semver": "^6.3.0", + "table": "^6.8.0", + "undici": "^5.14.0" + }, + "peerDependencies": { + "hardhat": "^2.0.4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/hardhat-verify/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@nomicfoundation/ignition-core": { + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-core/-/ignition-core-0.15.7.tgz", + "integrity": "sha512-C4/0V/q2gNxKDt88cMr+Oxlf4NINQ7QgmJyciQ1/6UdCRUg+/Pgdgpd3vgGXQVTotq50Q/BU4ofNUAD/8HRqtg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/address": "5.6.1", + "@nomicfoundation/solidity-analyzer": "^0.1.1", + "cbor": "^9.0.0", + "debug": "^4.3.2", + "ethers": "^6.7.0", + "fs-extra": "^10.0.0", + "immer": "10.0.2", + "lodash": "4.17.21", + "ndjson": "2.0.0" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/@ethersproject/address": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.1.tgz", + "integrity": "sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.6.2", + "@ethersproject/bytes": "^5.6.1", + "@ethersproject/keccak256": "^5.6.1", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/rlp": "^5.6.1" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@nomicfoundation/ignition-ui": { + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-ui/-/ignition-ui-0.15.7.tgz", + "integrity": "sha512-pj2LmXylgbHOTNrkFqFrre/FAOjcwYl4VKIKVH/QMMBH/DatbiT8aC5n9o2fbLD8uwlPEesD+uXZuKCE71KFBg==", + "dev": true, + "peer": true + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", + "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.2", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.2" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz", + "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz", + "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz", + "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz", + "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz", + "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz", + "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz", + "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@openzeppelin/contracts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.1.0.tgz", + "integrity": "sha512-p1ULhl7BXzjjbha5aqst+QMLY+4/LCWADXOCsmLHRM77AqiPjnd9vvUN9sosUfhL9JGKpZ0TjEGxgvnizmWGSA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@solidity-parser/parser": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", + "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@typechain/ethers-v6": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", + "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + }, + "peerDependencies": { + "ethers": "6.x", + "typechain": "^8.3.2", + "typescript": ">=4.7.0" + } + }, + "node_modules/@typechain/hardhat": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-9.1.0.tgz", + "integrity": "sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^9.1.0" + }, + "peerDependencies": { + "@typechain/ethers-v6": "^0.5.1", + "ethers": "^6.1.0", + "hardhat": "^2.9.9", + "typechain": "^8.3.2" + } + }, + "node_modules/@typechain/hardhat/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typechain/hardhat/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@typechain/hardhat/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz", + "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "peer": true + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/mocha": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", + "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/node": { + "version": "20.12.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz", + "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "license": "BSD-3-Clause OR MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "peer": true, + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "peer": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "peer": true + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "peer": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz", + "integrity": "sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", + "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", + "dev": true, + "peer": true, + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@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", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@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" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base-x": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", + "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "dev": true, + "license": "WTFPL", + "peer": true, + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 6" + } + }, + "node_modules/chai/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cli-table3/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/command-line-usage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/command-line-usage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/command-line-usage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/command-line-usage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "peer": true + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", + "dev": true, + "peer": true + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "peer": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/difflib": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", + "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", + "dev": true, + "peer": true, + "dependencies": { + "heap": ">= 0.2.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.11.tgz", + "integrity": "sha512-R1CccCDYqndR25CaXFd6hp/u9RaaMcftMkphmvuepXr5b1vfLkRml6aWVeBhXJ7rbevHkKEMJtz8XqPf7ffmew==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "peer": true + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "peer": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "peer": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "peer": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "peer": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/escodegen/node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + }, + "engines": { + "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz", + "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.12.0", + "eslint-module-utils": "^2.7.4", + "fast-glob": "^3.3.1", + "get-tsconfig": "^4.5.0", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "peer": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz", + "integrity": "sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==", + "dev": true, + "peer": true, + "dependencies": { + "aria-query": "~5.1.3", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.9.1", + "axobject-query": "~3.1.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.19", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.0" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.35.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", + "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", + "dev": true, + "peer": true, + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.19", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "peer": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eth-gas-reporter": { + "version": "0.2.27", + "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz", + "integrity": "sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@solidity-parser/parser": "^0.14.0", + "axios": "^1.5.1", + "cli-table3": "^0.5.0", + "colors": "1.4.0", + "ethereum-cryptography": "^1.0.3", + "ethers": "^5.7.2", + "fs-readdir-recursive": "^1.1.0", + "lodash": "^4.17.14", + "markdown-table": "^1.1.3", + "mocha": "^10.2.0", + "req-cwd": "^2.0.0", + "sha1": "^1.1.1", + "sync-request": "^6.0.0" + }, + "peerDependencies": { + "@codechecks/client": "^0.1.0" + }, + "peerDependenciesMeta": { + "@codechecks/client": { + "optional": true + } + } + }, + "node_modules/eth-gas-reporter/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, + "node_modules/ethereum-bloom-filters": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.2.0.tgz", + "integrity": "sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "^1.4.0" + } + }, + "node_modules/ethereum-bloom-filters/node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "deprecated": "This library has been deprecated and usage is discouraged.", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethers": { + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz", + "integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", + "dev": true + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "peer": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.6.tgz", + "integrity": "sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/ghost-testrpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", + "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "chalk": "^2.4.2", + "node-emoji": "^1.10.0" + }, + "bin": { + "testrpc-sc": "index.js" + } + }, + "node_modules/ghost-testrpc/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ghost-testrpc/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ghost-testrpc/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ghost-testrpc/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "peer": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hardhat": { + "version": "2.22.15", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.15.tgz", + "integrity": "sha512-BpTGa9PE/sKAaHi4s/S1e9WGv63DR1m7Lzfd60C8gSEchDPfAJssVRSq0MZ2v2k76ig9m0kHAwVLf5teYwu/Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/edr": "^0.6.4", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-tx": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "^5.1.0", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", + "chalk": "^2.4.2", + "chokidar": "^4.0.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^2.1.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "glob": "7.2.0", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "json-stream-stringify": "^3.1.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.8.26", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tsort": "0.0.1", + "undici": "^5.14.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/bootstrap.js" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat-gas-reporter": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.10.tgz", + "integrity": "sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-uniq": "1.0.3", + "eth-gas-reporter": "^0.2.25", + "sha1": "^1.1.1" + }, + "peerDependencies": { + "hardhat": "^2.0.2" + } + }, + "node_modules/hardhat/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/hardhat/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/hardhat/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hardhat/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/hardhat/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/hardhat/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hardhat/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "peer": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.2.tgz", + "integrity": "sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "peer": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "peer": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "peer": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "peer": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "peer": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "peer": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "peer": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "peer": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "peer": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "peer": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "peer": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "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.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@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.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@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.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "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/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stream-stringify": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", + "integrity": "sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=7.10.1" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "peer": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "peer": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "peer": true + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "peer": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/markdown-table": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/ndjson": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz", + "integrity": "sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "json-stringify-safe": "^5.0.1", + "minimist": "^1.2.5", + "readable-stream": "^3.6.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "ndjson": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ordinal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", + "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true, + "peer": true + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "peer": true + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "peer": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/req-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", + "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "req-from": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/req-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", + "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/req-from/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sc-istanbul": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", + "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/sc-istanbul/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/sc-istanbul/node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/sc-istanbul/node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sc-istanbul/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sc-istanbul/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sc-istanbul/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/sc-istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/sc-istanbul/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sc-istanbul/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true, + "license": "MIT" + }, + "node_modules/secp256k1": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "elliptic": "^6.5.7", + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/secp256k1/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/secp256k1/node_modules/elliptic": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/secp256k1/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "peer": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/solc": { + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "^8.1.0", + "follow-redirects": "^1.12.1", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solc.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solidity-coverage": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.13.tgz", + "integrity": "sha512-RiBoI+kF94V3Rv0+iwOj3HQVSqNzA9qm/qDP1ZDXK5IX0Cvho1qiz8hAXTsAo6KOIUeP73jfscq0KlLqVxzGWA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.0.9", + "@solidity-parser/parser": "^0.18.0", + "chalk": "^2.4.2", + "death": "^1.1.0", + "difflib": "^0.2.4", + "fs-extra": "^8.1.0", + "ghost-testrpc": "^0.0.2", + "global-modules": "^2.0.0", + "globby": "^10.0.1", + "jsonschema": "^1.2.4", + "lodash": "^4.17.21", + "mocha": "^10.2.0", + "node-emoji": "^1.10.0", + "pify": "^4.0.1", + "recursive-readdir": "^2.2.2", + "sc-istanbul": "^0.4.5", + "semver": "^7.3.4", + "shelljs": "^0.8.3", + "web3-utils": "^1.3.6" + }, + "bin": { + "solidity-coverage": "plugins/bin.js" + }, + "peerDependencies": { + "hardhat": "^2.11.0" + } + }, + "node_modules/solidity-coverage/node_modules/@solidity-parser/parser": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/solidity-coverage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/solidity-coverage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/solidity-coverage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/solidity-coverage/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/solidity-coverage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-coverage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "peer": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true, + "license": "WTFPL OR MIT" + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string.prototype.includes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz", + "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==", + "dev": true, + "peer": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "peer": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/synckit": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, + "node_modules/table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/then-request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-command-line-args": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", + "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "bin": { + "write-markdown": "dist/write-markdown.js" + } + }, + "node_modules/ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=3.7.0" + } + }, + "node_modules/ts-jest": { + "version": "29.2.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz", + "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typechain": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prettier": "^2.1.1", + "debug": "^4.3.1", + "fs-extra": "^7.0.0", + "glob": "7.1.7", + "js-sha3": "^0.8.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "prettier": "^2.3.1", + "ts-command-line-args": "^2.2.0", + "ts-essentials": "^7.0.1" + }, + "bin": { + "typechain": "dist/cli/cli.js" + }, + "peerDependencies": { + "typescript": ">=4.3.0" + } + }, + "node_modules/typechain/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "peer": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/web3-utils": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", + "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", + "dev": true, + "license": "LGPL-3.0", + "peer": true, + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "peer": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "peer": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "peer": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "peer": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dev": true, + "license": "MIT", + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "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.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/evm-e2e/package.json b/evm-e2e/package.json new file mode 100644 index 000000000..292d7b972 --- /dev/null +++ b/evm-e2e/package.json @@ -0,0 +1,38 @@ +{ + "name": "nibiru-evm-test", + "version": "0.0.1", + "description": "Nibiru EVM tests", + "keywords": [], + "author": "Nibiru Team", + "license": "ISC", + "engines": { + "node": ">=0.18.0" + }, + "devDependencies": { + "@jest/globals": "^29.7.0", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", + "@types/jest": "^29.5.12", + "dotenv": "^16.4.5", + "eslint": "^8.0.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-prettier": "^5.1.3", + "ethers": "^6.12.1", + "jest": "^29.7.0", + "prettier": "^3.3.3", + "ts-jest": "^29.2.4", + "typechain": "^8.3.2", + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@openzeppelin/contracts": "^5.1.0", + "@nibiruchain/solidity": "^0.0.2", + "hardhat": "^2.22.15" + }, + "scripts": { + "test": "jest", + "format": "prettier --write \"test/**/*.ts\"", + "format:check": "prettier --check \"test/**/*.ts\"" + } +} diff --git a/evm-e2e/prettier.config.mjs b/evm-e2e/prettier.config.mjs new file mode 100644 index 000000000..97a05abd3 --- /dev/null +++ b/evm-e2e/prettier.config.mjs @@ -0,0 +1,13 @@ +// prettier.config.js, .prettierrc.js, prettier.config.mjs, or .prettierrc.mjs + +/** @type {import("prettier").Config} */ +const config = { + trailingComma: "all", + tabWidth: 2, + printWidth: 80, + semi: false, + singleQuote: false, + arrowParens: "always", +} + +export default config diff --git a/evm-e2e/test/contract_infinite_loop_gas.test.ts b/evm-e2e/test/contract_infinite_loop_gas.test.ts new file mode 100644 index 000000000..ac1350811 --- /dev/null +++ b/evm-e2e/test/contract_infinite_loop_gas.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from '@jest/globals'; +import { toBigInt } from 'ethers'; +import { deployContractInfiniteLoopGas } from './utils'; +import { TEST_TIMEOUT } from './setup'; + +describe('Infinite loop gas contract', () => { + it( + 'should fail due to out of gas error', + async () => { + const contract = await deployContractInfiniteLoopGas(); + + await expect(contract.counter()).resolves.toBe(toBigInt(0)); + + try { + const tx = await contract.forever({ gasLimit: 1e6 }); + await tx.wait(); + throw new Error('The transaction should have failed but did not.'); + } catch (error) { + expect(error.message).toContain('transaction execution reverted'); + } + + await expect(contract.counter()).resolves.toBe(toBigInt(0)); + }, + TEST_TIMEOUT, + ); +}); diff --git a/evm-e2e/test/contract_send_nibi.test.ts b/evm-e2e/test/contract_send_nibi.test.ts new file mode 100644 index 000000000..da322fba8 --- /dev/null +++ b/evm-e2e/test/contract_send_nibi.test.ts @@ -0,0 +1,81 @@ +/** + * @file sendNibi.test.ts + * + * This test suite is designed to validate the functionality of the for sending + * NIBI via various mechanisms like transfer, send, and call. The tests ensure + * that the correct amount of NIBI is transferred and that balances are updated + * accordingly. + * + * The methods tested are from the smart contract, + * "evm-e2e/contracts/SendReceiveNibi.sol". + */ +import { describe, expect, it } from '@jest/globals'; +import { parseEther, toBigInt, Wallet } from 'ethers'; +import { account, provider, TEST_TIMEOUT, TX_WAIT_TIMEOUT } from './setup'; +import { deployContractSendNibi } from './utils'; + +async function testSendNibi(method: 'sendViaTransfer' | 'sendViaSend' | 'sendViaCall', weiToSend: bigint) { + const contract = await deployContractSendNibi(); + const recipient = Wallet.createRandom(); + + const ownerBalanceBefore = await provider.getBalance(account); + const recipientBalanceBefore = await provider.getBalance(recipient); + expect(recipientBalanceBefore).toEqual(BigInt(0)); + + const tx = await contract[method](recipient, { value: weiToSend }); + const receipt = await tx.wait(1, TX_WAIT_TIMEOUT); + + const tenPow12 = toBigInt(1e12); + const txCostMicronibi = weiToSend / tenPow12 + receipt.gasUsed; + const txCostWei = txCostMicronibi * tenPow12; + const expectedOwnerWei = ownerBalanceBefore - txCostWei; + + const ownerBalanceAfter = await provider.getBalance(account); + const recipientBalanceAfter = await provider.getBalance(recipient); + + console.debug(`DEBUG method ${method} %o:`, { + ownerBalanceBefore, + weiToSend, + expectedOwnerWei, + ownerBalanceAfter, + recipientBalanceBefore, + recipientBalanceAfter, + gasUsed: receipt.gasUsed, + gasPrice: `${receipt.gasPrice.toString()}`, + to: receipt.to, + from: receipt.from, + }); + expect(recipientBalanceAfter).toBe(weiToSend); + const delta = ownerBalanceAfter - expectedOwnerWei; + const deltaFromExpectation = delta >= 0 ? delta : -delta; + expect(deltaFromExpectation).toBeLessThan(parseEther('0.1')); +} + +describe('Send NIBI via smart contract', () => { + it( + 'method sendViaTransfer', + async () => { + const weiToSend: bigint = toBigInt(5e12) * toBigInt(1e6); + await testSendNibi('sendViaTransfer', weiToSend); + }, + TEST_TIMEOUT, + ); + + it( + 'method sendViaSend', + async () => { + const weiToSend: bigint = toBigInt(100e12) * toBigInt(1e6); + await testSendNibi('sendViaSend', weiToSend); + }, + TEST_TIMEOUT, + ); + + it( + 'method sendViaCall', + async () => { + const weiToSend: bigint = toBigInt(100e12) * toBigInt(1e6); + await testSendNibi('sendViaCall', weiToSend); + }, + TEST_TIMEOUT, + ); +}); diff --git a/evm-e2e/test/debug_queries.test.ts b/evm-e2e/test/debug_queries.test.ts new file mode 100644 index 000000000..3c6468504 --- /dev/null +++ b/evm-e2e/test/debug_queries.test.ts @@ -0,0 +1,125 @@ +import { describe, expect, it, beforeAll } from '@jest/globals'; +import { TransactionReceipt, parseEther } from 'ethers'; +import { provider, TEST_TIMEOUT, TX_WAIT_TIMEOUT } from './setup'; +import { alice, deployContractTestERC20, hexify } from './utils'; +import { TestERC20__factory } from '../types'; + +describe('debug queries', () => { + let contractAddress: string; + let txHash: string; + let txIndex: number; + let blockNumber: number; + let blockHash: string; + + beforeAll(async () => { + // Deploy ERC-20 contract + const contract = await deployContractTestERC20(); + contractAddress = await contract.getAddress(); + + // Execute some contract TX + const txResponse = await contract.transfer(alice, parseEther('0.01')); + await txResponse.wait(1, TX_WAIT_TIMEOUT); + + const receipt: TransactionReceipt = await provider.getTransactionReceipt(txResponse.hash); + txHash = txResponse.hash; + txIndex = txResponse.index; + blockNumber = receipt.blockNumber; + blockHash = receipt.blockHash; + }, TEST_TIMEOUT); + + it('debug_traceBlockByNumber', async () => { + const traceResult = await provider.send('debug_traceBlockByNumber', [ + blockNumber, + { + tracer: 'callTracer', + timeout: '3000s', + tracerConfig: { onlyTopCall: false }, + }, + ]); + expectTrace(traceResult); + }); + + it('debug_traceBlockByHash', async () => { + const traceResult = await provider.send('debug_traceBlockByHash', [ + blockHash, + { + tracer: 'callTracer', + timeout: '3000s', + tracerConfig: { onlyTopCall: false }, + }, + ]); + expectTrace(traceResult); + }); + + it('debug_traceTransaction', async () => { + const traceResult = await provider.send('debug_traceTransaction', [ + txHash, + { + tracer: 'callTracer', + timeout: '3000s', + tracerConfig: { onlyTopCall: false }, + }, + ]); + expectTrace([{ result: traceResult }]); + }); + + it('debug_traceCall', async () => { + const contractInterface = TestERC20__factory.createInterface(); + const callData = contractInterface.encodeFunctionData('totalSupply'); + const tx = { + to: contractAddress, + data: callData, + gas: hexify(1000_000), + }; + const traceResult = await provider.send('debug_traceCall', [ + tx, + 'latest', + { + tracer: 'callTracer', + timeout: '3000s', + tracerConfig: { onlyTopCall: false }, + }, + ]); + expectTrace([{ result: traceResult }]); + }); + + // TODO: impl in EVM + it('debug_getBadBlocks', async () => { + try { + const traceResult = await provider.send('debug_getBadBlocks', [txHash]); + expect(traceResult).toBeDefined(); + } catch (err) { + expect(err.message).toContain('the method debug_getBadBlocks does not exist'); + } + }); + + // TODO: impl in EVM + it('debug_storageRangeAt', async () => { + try { + const traceResult = await provider.send('debug_storageRangeAt', [ + blockNumber, + txIndex, + contractAddress, + '0x0', + 100, + ]); + expect(traceResult).toBeDefined(); + } catch (err) { + expect(err.message).toContain('the method debug_storageRangeAt does not exist'); + } + }); +}); + +const expectTrace = (traceResult: any[]) => { + expect(traceResult).toBeDefined(); + expect(traceResult.length).toBeGreaterThan(0); + + const trace = traceResult[0]['result']; + expect(trace).toHaveProperty('from'); + expect(trace).toHaveProperty('to'); + expect(trace).toHaveProperty('gas'); + expect(trace).toHaveProperty('gasUsed'); + expect(trace).toHaveProperty('input'); + expect(trace).toHaveProperty('output'); + expect(trace).toHaveProperty('type', 'CALL'); +}; diff --git a/evm-e2e/test/erc20.test.ts b/evm-e2e/test/erc20.test.ts new file mode 100644 index 000000000..573f23d20 --- /dev/null +++ b/evm-e2e/test/erc20.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from '@jest/globals'; +import { parseUnits, toBigInt, Wallet } from 'ethers'; +import { account, TEST_TIMEOUT } from './setup'; +import { deployContractTestERC20 } from './utils'; + +describe('ERC-20 contract tests', () => { + it( + 'should send properly', + async () => { + const contract = await deployContractTestERC20(); + expect(contract.getAddress()).resolves.toBeDefined(); + + const ownerInitialBalance = parseUnits('1000000', 18); + const alice = Wallet.createRandom(); + + expect(contract.balanceOf(account)).resolves.toEqual(ownerInitialBalance); + expect(contract.balanceOf(alice)).resolves.toEqual(toBigInt(0)); + + // send to alice + const amountToSend = parseUnits('1000', 18); // contract tokens + let tx = await contract.transfer(alice, amountToSend); + await tx.wait(); + + expect(contract.balanceOf(account)).resolves.toEqual(ownerInitialBalance - amountToSend); + expect(contract.balanceOf(alice)).resolves.toEqual(amountToSend); + }, + TEST_TIMEOUT, + ); +}); diff --git a/evm-e2e/test/eth_queries.test.ts b/evm-e2e/test/eth_queries.test.ts new file mode 100644 index 000000000..fc8ccac0b --- /dev/null +++ b/evm-e2e/test/eth_queries.test.ts @@ -0,0 +1,268 @@ +import { describe, expect, it, jest } from '@jest/globals'; +import { parseEther, keccak256, AbiCoder, ethers } from 'ethers'; +import { account, provider, TEST_TIMEOUT, TX_WAIT_TIMEOUT } from './setup'; +import { + INTRINSIC_TX_GAS, + alice, + deployContractTestERC20, + deployContractSendNibi, + hexify, + sendTestNibi, + numberToHex, +} from './utils'; + +describe('eth queries', () => { + jest.setTimeout(TEST_TIMEOUT); + + it('eth_accounts', async () => { + const accounts = await provider.listAccounts(); + expect(accounts).not.toBeNull(); + }); + + it('eth_estimateGas', async () => { + const tx = { + from: account.address, + to: alice, + value: parseEther('0.01'), // Sending 0.01 Ether + }; + const estimatedGas = await provider.estimateGas(tx); + expect(estimatedGas).toBeGreaterThan(BigInt(0)); + expect(estimatedGas).toEqual(INTRINSIC_TX_GAS); + }); + + it('eth_feeHistory', async () => { + const blockCount = 5; // Number of blocks in the requested history + const newestBlock = 'latest'; // Can be a block number or 'latest' + const rewardPercentiles = [25, 50, 75]; // Example percentiles for priority fees + + const feeHistory = await provider.send('eth_feeHistory', [blockCount, newestBlock, rewardPercentiles]); + expect(feeHistory).toBeDefined(); + expect(feeHistory).toHaveProperty('baseFeePerGas'); + expect(feeHistory).toHaveProperty('gasUsedRatio'); + expect(feeHistory).toHaveProperty('oldestBlock'); + expect(feeHistory).toHaveProperty('reward'); + }); + + it('eth_gasPrice', async () => { + const gasPrice = await provider.send('eth_gasPrice', []); + expect(gasPrice).toBeDefined(); + expect(gasPrice).toEqual(hexify(1000000000000)); // 1 micronibi == 10^{12} wei + }); + + it('eth_getBalance', async () => { + const balance = await provider.getBalance(account.address); + expect(balance).toBeGreaterThan(0); + }); + + it('eth_getBlockByNumber, eth_getBlockByHash', async () => { + const blockNumber = 'latest'; + const blockByNumber = await provider.send('eth_getBlockByNumber', [blockNumber, false]); + expect(blockByNumber).toBeDefined(); + expect(blockByNumber).toHaveProperty('hash'); + + const blockByHash = await provider.send('eth_getBlockByHash', [blockByNumber.hash, false]); + expect(blockByHash).toBeDefined(); + expect(blockByHash.hash).toEqual(blockByNumber.hash); + expect(blockByHash.number).toEqual(blockByNumber.number); + }); + + it('eth_getBlockTransactionCountByHash', async () => { + const blockNumber = 'latest'; + const block = await provider.send('eth_getBlockByNumber', [blockNumber, false]); + const txCount = await provider.send('eth_getBlockTransactionCountByHash', [block.hash]); + expect(parseInt(txCount)).toBeGreaterThanOrEqual(0); + }); + + it('eth_getBlockTransactionCountByNumber', async () => { + const blockNumber = 'latest'; + const txCount = await provider.send('eth_getBlockTransactionCountByNumber', [blockNumber]); + expect(parseInt(txCount)).toBeGreaterThanOrEqual(0); + }); + + it('eth_getCode', async () => { + const contract = await deployContractSendNibi(); + const contractAddr = await contract.getAddress(); + const code = await provider.send('eth_getCode', [contractAddr, 'latest']); + expect(code).toBeDefined(); + }); + + it('eth_getFilterChanges', async () => { + const currentBlock = await provider.getBlockNumber(); + // Deploy ERC-20 contract + const contract = await deployContractTestERC20(); + const contractAddr = await contract.getAddress(); + const filter = { + fromBlock: numberToHex(currentBlock), + address: contractAddr, + }; + // Create the filter for a contract + const filterId = await provider.send('eth_newFilter', [filter]); + expect(filterId).toBeDefined(); + + // Execute some contract TX + const tx = await contract.transfer(alice, parseEther('0.01')); + await tx.wait(1, TX_WAIT_TIMEOUT); + await new Promise((resolve) => setTimeout(resolve, 3000)); + + // Assert logs + const changes = await provider.send('eth_getFilterChanges', [filterId]); + expect(changes.length).toBeGreaterThan(0); + expect(changes[0]).toHaveProperty('address'); + expect(changes[0]).toHaveProperty('data'); + expect(changes[0]).toHaveProperty('topics'); + + const success = await provider.send('eth_uninstallFilter', [filterId]); + expect(success).toBeTruthy(); + }); + + it('eth_getFilterLogs', async () => { + const currentBlock = await provider.getBlockNumber(); + // Deploy ERC-20 contract + const contract = await deployContractTestERC20(); + const contractAddr = await contract.getAddress(); + const filter = { + fromBlock: numberToHex(currentBlock), + address: contractAddr, + }; + // Execute some contract TX + const tx = await contract.transfer(alice, parseEther('0.01')); + await tx.wait(1, TX_WAIT_TIMEOUT); + await new Promise((resolve) => setTimeout(resolve, 3000)); + + // Create the filter for a contract + const filterId = await provider.send('eth_newFilter', [filter]); + expect(filterId).toBeDefined(); + + // Assert logs + const changes = await provider.send('eth_getFilterLogs', [filterId]); + expect(changes.length).toBeGreaterThan(0); + expect(changes[0]).toHaveProperty('address'); + expect(changes[0]).toHaveProperty('data'); + expect(changes[0]).toHaveProperty('topics'); + }); + + it('eth_getLogs', async () => { + const currentBlock = await provider.getBlockNumber(); + // Deploy ERC-20 contract + const contract = await deployContractTestERC20(); + const contractAddr = await contract.getAddress(); + const filter = { + fromBlock: numberToHex(currentBlock), + address: contractAddr, + }; + // Execute some contract TX + const tx = await contract.transfer(alice, parseEther('0.01')); + await tx.wait(1, TX_WAIT_TIMEOUT); + await new Promise((resolve) => setTimeout(resolve, 3000)); + + // Assert logs + const changes = await provider.send('eth_getLogs', [filter]); + expect(changes.length).toBeGreaterThan(0); + expect(changes[0]).toHaveProperty('address'); + expect(changes[0]).toHaveProperty('data'); + expect(changes[0]).toHaveProperty('topics'); + }); + + it('eth_getProof', async () => { + // Deploy ERC-20 contract + const contract = await deployContractTestERC20(); + const contractAddr = await contract.getAddress(); + + const slot = 1; // Assuming balanceOf is at slot 1 + const storageKey = keccak256(AbiCoder.defaultAbiCoder().encode(['address', 'uint256'], [account.address, slot])); + const proof = await provider.send('eth_getProof', [contractAddr, [storageKey], 'latest']); + // Assert proof structure + expect(proof).toHaveProperty('address'); + expect(proof).toHaveProperty('balance'); + expect(proof).toHaveProperty('codeHash'); + expect(proof).toHaveProperty('nonce'); + expect(proof).toHaveProperty('storageProof'); + + if (proof.storageProof.length > 0) { + expect(proof.storageProof[0]).toHaveProperty('key', storageKey); + expect(proof.storageProof[0]).toHaveProperty('value'); + expect(proof.storageProof[0]).toHaveProperty('proof'); + } + }); + + it('eth_getStorageAt', async () => { + const contract = await deployContractTestERC20(); + const contractAddr = await contract.getAddress(); + + const value = await provider.getStorage(contractAddr, 1); + expect(value).toBeDefined(); + }); + + it('eth_getTransactionByBlockHashAndIndex, eth_getTransactionByBlockNumberAndIndex', async () => { + // Execute EVM transfer + const txResponse = await sendTestNibi(); + const block = await txResponse.getBlock(); + + const txByBlockHash = await provider.send('eth_getTransactionByBlockHashAndIndex', [block.hash, '0x0']); + expect(txByBlockHash).toBeDefined(); + expect(txByBlockHash).toHaveProperty('from'); + expect(txByBlockHash).toHaveProperty('to'); + expect(txByBlockHash).toHaveProperty('blockHash'); + expect(txByBlockHash).toHaveProperty('blockNumber'); + expect(txByBlockHash).toHaveProperty('value'); + + const txByBlockNumber = await provider.send('eth_getTransactionByBlockNumberAndIndex', [block.number, '0x0']); + + expect(txByBlockNumber).toBeDefined(); + expect(txByBlockNumber['from']).toEqual(txByBlockHash['from']); + expect(txByBlockNumber['to']).toEqual(txByBlockHash['to']); + expect(txByBlockNumber['value']).toEqual(txByBlockHash['value']); + }); + + it('eth_getTransactionByHash', async () => { + const txResponse = await sendTestNibi(); + const txByHash = await provider.getTransaction(txResponse.hash); + expect(txByHash).toBeDefined(); + expect(txByHash.hash).toEqual(txResponse.hash); + }); + + it('eth_getTransactionCount', async () => { + const txCount = await provider.getTransactionCount(account.address); + expect(txCount).toBeGreaterThanOrEqual(0); + }); + + it('eth_getTransactionReceipt', async () => { + const txResponse = await sendTestNibi(); + const txReceipt = await provider.getTransactionReceipt(txResponse.hash); + expect(txReceipt).toBeDefined(); + expect(txReceipt.hash).toEqual(txResponse.hash); + }); + + it('eth_getUncleCountByBlockHash', async () => { + const latestBlock = await provider.getBlockNumber(); + const block = await provider.getBlock(latestBlock); + const uncleCount = await provider.send('eth_getUncleCountByBlockHash', [block.hash]); + expect(parseInt(uncleCount)).toBeGreaterThanOrEqual(0); + }); + + it('eth_getUncleCountByBlockNumber', async () => { + const latestBlock = await provider.getBlockNumber(); + const uncleCount = await provider.send('eth_getUncleCountByBlockNumber', [latestBlock]); + expect(parseInt(uncleCount)).toBeGreaterThanOrEqual(0); + }); + + it('eth_maxPriorityFeePerGas', async () => { + const maxPriorityGas = await provider.send('eth_maxPriorityFeePerGas', []); + expect(parseInt(maxPriorityGas)).toBeGreaterThanOrEqual(0); + }); + + it('eth_newBlockFilter', async () => { + const filterId = await provider.send('eth_newBlockFilter', []); + expect(filterId).toBeDefined(); + }); + + it('eth_newPendingTransactionFilter', async () => { + const filterId = await provider.send('eth_newPendingTransactionFilter', []); + expect(filterId).toBeDefined(); + }); + + it('eth_syncing', async () => { + const syncing = await provider.send('eth_syncing', []); + expect(syncing).toBeFalsy(); + }); +}); diff --git a/evm-e2e/test/native_transfer.test.ts b/evm-e2e/test/native_transfer.test.ts new file mode 100644 index 000000000..c6c0193f8 --- /dev/null +++ b/evm-e2e/test/native_transfer.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from '@jest/globals'; +import { parseEther, toBigInt } from 'ethers'; +import { account, provider, TEST_TIMEOUT, TX_WAIT_TIMEOUT } from './setup'; +import { alice } from './utils'; + +describe('native transfer', () => { + it( + 'simple transfer, balance check', + async () => { + const amountToSend = toBigInt(5e12) * toBigInt(1e6); // unibi + const senderBalanceBefore = await provider.getBalance(account); + const recipientBalanceBefore = await provider.getBalance(alice); + expect(senderBalanceBefore).toBeGreaterThan(0); + expect(recipientBalanceBefore).toEqual(BigInt(0)); + + // Execute EVM transfer + const transaction = { + gasLimit: toBigInt(100e3), + to: alice, + value: amountToSend, + }; + const txResponse = await account.sendTransaction(transaction); + await txResponse.wait(1, TX_WAIT_TIMEOUT); + expect(txResponse).toHaveProperty('blockHash'); + + const senderBalanceAfter = await provider.getBalance(account); + const recipientBalanceAfter = await provider.getBalance(alice); + + // Assert balances with logging + const tenPow12 = toBigInt(1e12); + const gasUsed = transaction.gasLimit; + const txCostMicronibi = amountToSend / tenPow12 + gasUsed; + const txCostWei = txCostMicronibi * tenPow12; + const expectedSenderWei = senderBalanceBefore - txCostWei; + console.debug('DEBUG should send via transfer method %o:', { + senderBalanceBefore, + amountToSend, + expectedSenderWei, + senderBalanceAfter, + txResponse, + }); + expect(recipientBalanceAfter).toEqual(amountToSend); + const delta = senderBalanceAfter - expectedSenderWei; + const deltaFromExpectation = delta >= 0 ? delta : -delta; + expect(deltaFromExpectation).toBeLessThan(parseEther('0.1')); + }, + TEST_TIMEOUT, + ); +}); diff --git a/evm-e2e/test/nibiru_oracle.test.ts b/evm-e2e/test/nibiru_oracle.test.ts new file mode 100644 index 000000000..5695a60b7 --- /dev/null +++ b/evm-e2e/test/nibiru_oracle.test.ts @@ -0,0 +1,45 @@ +import { expect, test } from '@jest/globals'; +import { toBigInt } from 'ethers'; +import { deployContractNibiruOracleChainLinkLike } from './utils'; +import { TEST_TIMEOUT } from './setup'; + +test( + 'NibiruOracleChainLinkLike implements ChainLink AggregatorV3Interface', + async () => { + const { oraclePair, contract } = await deployContractNibiruOracleChainLinkLike(); + + const oracleAddr = await contract.getAddress(); + expect(oracleAddr).not.toBeFalsy(); + + const decimals = await contract.decimals(); + expect(decimals).toEqual(BigInt(8)); + + const description = await contract.description(); + expect(description).toEqual(`Nibiru Oracle ChainLink-like price feed for ${oraclePair}`); + + const version = await contract.version(); + expect(version).toEqual(1n); + + // latestRoundData + const genesisEthUsdPrice = 2000n; + { + const { roundId, answer, startedAt, updatedAt, answeredInRound } = await contract.latestRoundData(); + expect(roundId).toEqual(0n); // price is from genesis block + expect(startedAt).toBeGreaterThan(1n); + expect(updatedAt).toBeGreaterThan(1n); + expect(answeredInRound).toEqual(420n); + expect(answer).toEqual(genesisEthUsdPrice * toBigInt(1e8)); + } + + // getRoundData + { + const { roundId, answer, startedAt, updatedAt, answeredInRound } = await contract.getRoundData(0n); + expect(roundId).toEqual(0n); // price is from genesis block + expect(startedAt).toBeGreaterThan(1n); + expect(updatedAt).toBeGreaterThan(1n); + expect(answeredInRound).toEqual(420n); + expect(answer).toEqual(genesisEthUsdPrice * toBigInt(1e8)); + } + }, + TEST_TIMEOUT, +); diff --git a/evm-e2e/test/setup.ts b/evm-e2e/test/setup.ts new file mode 100644 index 000000000..09fdc0784 --- /dev/null +++ b/evm-e2e/test/setup.ts @@ -0,0 +1,11 @@ +import { config } from 'dotenv'; +import { ethers, getDefaultProvider, Wallet } from 'ethers'; + +config(); + +const provider = new ethers.JsonRpcProvider(process.env.JSON_RPC_ENDPOINT); +const account = Wallet.fromPhrase(process.env.MNEMONIC, provider); +const TEST_TIMEOUT = Number(process.env.TEST_TIMEOUT) || 15000; +const TX_WAIT_TIMEOUT = Number(process.env.TX_WAIT_TIMEOUT) || 5000; + +export { account, provider, TEST_TIMEOUT, TX_WAIT_TIMEOUT }; diff --git a/evm-e2e/test/tx_receipt.test.ts b/evm-e2e/test/tx_receipt.test.ts new file mode 100644 index 000000000..c42f1bb41 --- /dev/null +++ b/evm-e2e/test/tx_receipt.test.ts @@ -0,0 +1,123 @@ +import { describe, expect, it, jest } from '@jest/globals'; +import { ethers, TransactionReceipt, Log } from 'ethers'; +import { account, TEST_TIMEOUT } from './setup'; +import { deployContractEventsEmitter } from './utils'; +import { TestERC20__factory } from '../types'; + +describe('Transaction Receipt Tests', () => { + jest.setTimeout(TEST_TIMEOUT); + + let recipient = ethers.Wallet.createRandom().address; + + it('simple transfer receipt', async () => { + const value = ethers.parseEther('0.0001'); + const tx = await account.sendTransaction({ + to: recipient, + value, + }); + const receipt = await tx.wait(); + + assertBaseReceiptFields(receipt); + expect(receipt.to).toEqual(recipient); + expect(receipt.logs).toHaveLength(0); // ETH transfers have no logs + }); + + it('contract deployment receipt', async () => { + const factory = new TestERC20__factory(account); + const deployTx = await factory.deploy(); + const receipt = await deployTx.deploymentTransaction().wait(); + + assertBaseReceiptFields(receipt); + expect(receipt.to).toBeNull(); // Contract creation has no 'to' address + expect(receipt.contractAddress).toBeDefined(); + + // Verify the deployed contract address is valid + const code = await account.provider.getCode(receipt.contractAddress!); + expect(code).not.toEqual('0x'); + }); + + it('receipt with logs / events', async () => { + const contract = await deployContractEventsEmitter(); + const expectedValue = 123n; + + const tx = await contract.emitEvent(expectedValue); + const receipt = await tx.wait(); + + assertBaseReceiptFields(receipt); + expect(receipt.to).toEqual(contract.target.toString()); + + // Event specific checks + expect(receipt.logs.length).toEqual(1); + const event = receipt.logs[0]; + assertEventLogFields(event, contract.target.toString()); + + // Event data checks + const eventSignature = 'TestEvent(address,uint256)'; + expect(event.topics[0]).toEqual(ethers.id(eventSignature)); + expect(event.topics.length).toEqual(2); // topic[0] is hash, topic[1] is indexed param + expect(event['args'].sender).toEqual(account.address); + expect(event['args'].value).toEqual(expectedValue); + + // Verify indexed parameter encoding + expect(event.topics[1]).toEqual(ethers.zeroPadValue(account.address, 32)); // indexed address param + }); +}); + +function assertBaseReceiptFields(receipt: TransactionReceipt) { + // Basic transaction info + expect(receipt.status).toEqual(1); + expect([0, 1, 2]).toContain(receipt.type); + expect(receipt.hash).toMatch(/^0x[a-fA-F0-9]{64}$/); + expect(typeof receipt.index).toBe('number'); + + // Block info + expect(typeof receipt.blockNumber).toBe('number'); + expect(receipt.blockNumber).toBeGreaterThan(0); + expect(receipt.blockHash).toMatch(/^0x[a-fA-F0-9]{64}$/); + + // Address fields + expect(receipt.from).toEqual(account.address); + expect(receipt.from).toMatch(/^0x[a-fA-F0-9]{40}$/); + + // Gas fields + expect(typeof receipt.gasUsed).toBe('bigint'); + expect(receipt.gasUsed).toBeGreaterThan(0n); + expect(typeof receipt.cumulativeGasUsed).toBe('bigint'); + expect(receipt.cumulativeGasUsed).toBeGreaterThanOrEqual(receipt.gasUsed); + + // Logs + expect(receipt.logsBloom).toMatch(/^0x[a-fA-F0-9]{512}$/); + expect(Array.isArray(receipt.logs)).toBeTruthy(); + + // Optional fields + if (receipt.to !== null) { + expect(receipt.to).toMatch(/^0x[a-fA-F0-9]{40}$/); + } + if (receipt.contractAddress !== null) { + expect(receipt.contractAddress).toMatch(/^0x[a-fA-F0-9]{40}$/); + } +} + +function assertEventLogFields(log: Log, expectedContractAddress: string) { + // Block info + expect(typeof log.blockNumber).toBe('number'); + expect(log.blockNumber).toBeGreaterThan(0); + expect(log.blockHash).toMatch(/^0x[a-fA-F0-9]{64}$/); + + // Transaction info + expect(typeof log.index).toBe('number'); + expect(log.transactionHash).toMatch(/^0x[a-fA-F0-9]{64}$/); + + // Address validation + expect(log.address).toEqual(expectedContractAddress); + expect(log.address).toMatch(/^0x[a-fA-F0-9]{40}$/); + + // Topics validation + expect(Array.isArray(log.topics)).toBeTruthy(); + log.topics.forEach((topic) => { + expect(topic).toMatch(/^0x[a-fA-F0-9]{64}$/); + }); + + // Data field + expect(log.data).toMatch(/^0x([a-fA-F0-9]{2})*$/); +} diff --git a/evm-e2e/test/utils.ts b/evm-e2e/test/utils.ts new file mode 100644 index 000000000..b6879d49a --- /dev/null +++ b/evm-e2e/test/utils.ts @@ -0,0 +1,83 @@ +import { account, provider, TX_WAIT_TIMEOUT } from './setup'; +import { ContractTransactionResponse, parseEther, toBigInt, TransactionRequest, Wallet } from 'ethers'; +import { + InifiniteLoopGas__factory, + SendNibi__factory, + TestERC20__factory, + EventsEmitter__factory, + TransactionReverter__factory, + NibiruOracleChainLinkLike__factory, + NibiruOracleChainLinkLike, +} from '../types'; + +export const alice = Wallet.createRandom(); + +export const hexify = (x: number): string => { + return '0x' + x.toString(16); +}; + +export const INTRINSIC_TX_GAS: bigint = 21000n; + +export const deployContractTestERC20 = async () => { + const factory = new TestERC20__factory(account); + const contract = await factory.deploy(); + await contract.waitForDeployment(); + return contract; +}; + +export const deployContractSendNibi = async () => { + const factory = new SendNibi__factory(account); + const contract = await factory.deploy(); + await contract.waitForDeployment(); + return contract; +}; + +export const deployContractInfiniteLoopGas = async () => { + const factory = new InifiniteLoopGas__factory(account); + const contract = await factory.deploy(); + await contract.waitForDeployment(); + return contract; +}; + +export const deployContractEventsEmitter = async () => { + const factory = new EventsEmitter__factory(account); + const contract = await factory.deploy(); + await contract.waitForDeployment(); + return contract; +}; + +export const deployContractTransactionReverter = async () => { + const factory = new TransactionReverter__factory(account); + const contract = await factory.deploy(); + await contract.waitForDeployment(); + return contract; +}; + +export const sendTestNibi = async () => { + const transaction: TransactionRequest = { + gasLimit: toBigInt(100e3), + to: alice, + value: parseEther('0.01'), + }; + const txResponse = await account.sendTransaction(transaction); + await txResponse.wait(1, TX_WAIT_TIMEOUT); + console.log(txResponse); + return txResponse; +}; + +export const deployContractNibiruOracleChainLinkLike = async (): Promise<{ + oraclePair: string; + contract: NibiruOracleChainLinkLike & { + deploymentTransaction(): ContractTransactionResponse; + }; +}> => { + const oraclePair = 'ueth:uuusd'; + const factory = new NibiruOracleChainLinkLike__factory(account); + const contract = await factory.deploy(oraclePair, toBigInt(8)); + await contract.waitForDeployment(); + return { oraclePair, contract }; +}; + +export const numberToHex = (num: Number) => { + return '0x' + num.toString(16); +}; diff --git a/evm-e2e/tsconfig.json b/evm-e2e/tsconfig.json new file mode 100644 index 000000000..35e3a5c59 --- /dev/null +++ b/evm-e2e/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "types": ["bun-types"], + "target": "es2020", + "moduleResolution": "node" + } +} diff --git a/evm-forge/.editorconfig b/evm-forge/.editorconfig new file mode 100644 index 000000000..746ae3127 --- /dev/null +++ b/evm-forge/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# All files +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.sol] +indent_size = 4 + +[*.tree] +indent_size = 1 diff --git a/evm-forge/.env.example b/evm-forge/.env.example new file mode 100644 index 000000000..98c102879 --- /dev/null +++ b/evm-forge/.env.example @@ -0,0 +1,11 @@ +export API_KEY_ALCHEMY="YOUR_API_KEY_ALCHEMY" +export API_KEY_ARBISCAN="YOUR_API_KEY_ARBISCAN" +export API_KEY_BSCSCAN="YOUR_API_KEY_BSCSCAN" +export API_KEY_ETHERSCAN="YOUR_API_KEY_ETHERSCAN" +export API_KEY_GNOSISSCAN="YOUR_API_KEY_GNOSISSCAN" +export API_KEY_INFURA="YOUR_API_KEY_INFURA" +export API_KEY_OPTIMISTIC_ETHERSCAN="YOUR_API_KEY_OPTIMISTIC_ETHERSCAN" +export API_KEY_POLYGONSCAN="YOUR_API_KEY_POLYGONSCAN" +export API_KEY_SNOWTRACE="YOUR_API_KEY_SNOWTRACE" +export MNEMONIC="YOUR_MNEMONIC" +export FOUNDRY_PROFILE="default" diff --git a/evm-forge/.github/FUNDING.yml b/evm-forge/.github/FUNDING.yml new file mode 100644 index 000000000..d8463c1d6 --- /dev/null +++ b/evm-forge/.github/FUNDING.yml @@ -0,0 +1,2 @@ +custom: "https://3cities.xyz/#/pay?c=CAESFAKY9DMuOFdjE4Wzl2YyUFipPiSfIgICATICCAJaFURvbmF0aW9uIHRvIFBhdWwgQmVyZw" +github: "PaulRBerg" diff --git a/evm-forge/.github/scripts/rename.sh b/evm-forge/.github/scripts/rename.sh new file mode 100755 index 000000000..62e37dda1 --- /dev/null +++ b/evm-forge/.github/scripts/rename.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# https://gist.github.com/vncsna/64825d5609c146e80de8b1fd623011ca +set -euo pipefail + +# Define the input vars +GITHUB_REPOSITORY=${1?Error: Please pass username/repo, e.g. prb/foundry-template} +GITHUB_REPOSITORY_OWNER=${2?Error: Please pass username, e.g. prb} +GITHUB_REPOSITORY_DESCRIPTION=${3:-""} # If null then replace with empty string + +echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY" +echo "GITHUB_REPOSITORY_OWNER: $GITHUB_REPOSITORY_OWNER" +echo "GITHUB_REPOSITORY_DESCRIPTION: $GITHUB_REPOSITORY_DESCRIPTION" + +# jq is like sed for JSON data +JQ_OUTPUT=`jq \ + --arg NAME "@$GITHUB_REPOSITORY" \ + --arg AUTHOR_NAME "$GITHUB_REPOSITORY_OWNER" \ + --arg URL "https://github.com/$GITHUB_REPOSITORY_OWNER" \ + --arg DESCRIPTION "$GITHUB_REPOSITORY_DESCRIPTION" \ + '.name = $NAME | .description = $DESCRIPTION | .author |= ( .name = $AUTHOR_NAME | .url = $URL )' \ + package.json +` + +# Overwrite package.json +echo "$JQ_OUTPUT" > package.json + +# Make sed command compatible in both Mac and Linux environments +# Reference: https://stackoverflow.com/a/38595160/8696958 +sedi () { + sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@" +} + +# Rename instances of "PaulRBerg/foundry-template" to the new repo name in README.md for badges only +sedi "/gitpod/ s|PaulRBerg/foundry-template|"${GITHUB_REPOSITORY}"|;" "README.md" +sedi "/gitpod-badge/ s|PaulRBerg/foundry-template|"${GITHUB_REPOSITORY}"|;" "README.md" +sedi "/gha/ s|PaulRBerg/foundry-template|"${GITHUB_REPOSITORY}"|;" "README.md" +sedi "/gha-badge/ s|PaulRBerg/foundry-template|"${GITHUB_REPOSITORY}"|;" "README.md" diff --git a/evm-forge/.github/workflows/ci.yml b/evm-forge/.github/workflows/ci.yml new file mode 100644 index 000000000..75507493e --- /dev/null +++ b/evm-forge/.github/workflows/ci.yml @@ -0,0 +1,92 @@ +name: "CI" + +env: + API_KEY_ALCHEMY: ${{ secrets.API_KEY_ALCHEMY }} + FOUNDRY_PROFILE: "ci" + +on: + workflow_dispatch: + pull_request: + push: + branches: + - "main" + +jobs: + lint: + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Install Bun" + uses: "oven-sh/setup-bun@v1" + + - name: "Install the Node.js dependencies" + run: "bun install" + + - name: "Lint the code" + run: "bun run lint" + + - name: "Add lint summary" + run: | + echo "## Lint result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + build: + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Install Bun" + uses: "oven-sh/setup-bun@v1" + + - name: "Install the Node.js dependencies" + run: "bun install" + + - name: "Build the contracts and print their size" + run: "forge build --sizes" + + - name: "Add build summary" + run: | + echo "## Build result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + test: + needs: ["lint", "build"] + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Install Bun" + uses: "oven-sh/setup-bun@v1" + + - name: "Install the Node.js dependencies" + run: "bun install" + + - name: "Show the Foundry config" + run: "forge config" + + - name: "Generate a fuzz seed that changes weekly to avoid burning through RPC allowance" + run: > + echo "FOUNDRY_FUZZ_SEED=$( + echo $(($EPOCHSECONDS - $EPOCHSECONDS % 604800)) + )" >> $GITHUB_ENV + + - name: "Run the tests" + run: "forge test" + + - name: "Add test summary" + run: | + echo "## Tests result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY diff --git a/evm-forge/.github/workflows/use-template.yml b/evm-forge/.github/workflows/use-template.yml new file mode 100644 index 000000000..183b17782 --- /dev/null +++ b/evm-forge/.github/workflows/use-template.yml @@ -0,0 +1,52 @@ +name: "Create" + +# The workflow will run when the "Use this template" button is used +on: + push: + +jobs: + create: + # We only run this action when the repository isn't the template repository. References: + # - https://docs.github.com/en/actions/learn-github-actions/contexts + # - https://docs.github.com/en/actions/learn-github-actions/expressions + if: ${{ !github.event.repository.is_template }} + permissions: "write-all" + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Update package.json" + env: + GITHUB_REPOSITORY_DESCRIPTION: ${{ github.event.repository.description }} + run: + ./.github/scripts/rename.sh "$GITHUB_REPOSITORY" "$GITHUB_REPOSITORY_OWNER" "$GITHUB_REPOSITORY_DESCRIPTION" + + - name: "Add rename summary" + run: | + echo "## Commit result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + - name: "Remove files not needed in the user's copy of the template" + run: | + rm -f "./.github/FUNDING.yml" + rm -f "./.github/scripts/rename.sh" + rm -f "./.github/workflows/create.yml" + + - name: "Add remove summary" + run: | + echo "## Remove result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY + + - name: "Update commit" + uses: "stefanzweifel/git-auto-commit-action@v4" + with: + commit_message: "feat: initial commit" + commit_options: "--amend" + push_options: "--force" + skip_fetch: true + + - name: "Add commit summary" + run: | + echo "## Commit result" >> $GITHUB_STEP_SUMMARY + echo "✅ Passed" >> $GITHUB_STEP_SUMMARY diff --git a/evm-forge/.gitignore b/evm-forge/.gitignore new file mode 100644 index 000000000..0521facf9 --- /dev/null +++ b/evm-forge/.gitignore @@ -0,0 +1,21 @@ +# directories +cache +coverage +node_modules +out +lib + +# files +*.env +*.log +.DS_Store +.pnp.* +lcov.info +package-lock.json +pnpm-lock.yaml +yarn.lock + +# broadcasts +!broadcast +broadcast/* +broadcast/*/31337/ diff --git a/evm-forge/.gitpod.yml b/evm-forge/.gitpod.yml new file mode 100644 index 000000000..b9646d8df --- /dev/null +++ b/evm-forge/.gitpod.yml @@ -0,0 +1,14 @@ +image: "gitpod/workspace-bun" + +tasks: + - name: "Install dependencies" + before: | + curl -L https://foundry.paradigm.xyz | bash + source ~/.bashrc + foundryup + init: "bun install" + +vscode: + extensions: + - "esbenp.prettier-vscode" + - "NomicFoundation.hardhat-solidity" diff --git a/evm-forge/.prettierignore b/evm-forge/.prettierignore new file mode 100644 index 000000000..3996d20be --- /dev/null +++ b/evm-forge/.prettierignore @@ -0,0 +1,17 @@ +# directories +broadcast +cache +coverage +node_modules +out + +# files +*.env +*.log +.DS_Store +.pnp.* +bun.lockb +lcov.info +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/evm-forge/.prettierrc.yml b/evm-forge/.prettierrc.yml new file mode 100644 index 000000000..a1ecdbb95 --- /dev/null +++ b/evm-forge/.prettierrc.yml @@ -0,0 +1,7 @@ +bracketSpacing: true +printWidth: 120 +proseWrap: "always" +singleQuote: false +tabWidth: 2 +trailingComma: "all" +useTabs: false diff --git a/evm-forge/.solhint.json b/evm-forge/.solhint.json new file mode 100644 index 000000000..14f780ec6 --- /dev/null +++ b/evm-forge/.solhint.json @@ -0,0 +1,14 @@ +{ + "extends": "solhint:recommended", + "rules": { + "code-complexity": ["error", 8], + "compiler-version": ["error", ">=0.8.25"], + "func-name-mixedcase": "off", + "func-visibility": ["error", { "ignoreConstructors": true }], + "max-line-length": ["error", 120], + "named-parameters-mapping": "warn", + "no-console": "off", + "not-rely-on-time": "off", + "one-contract-per-file": "off" + } +} diff --git a/evm-forge/LICENSE.md b/evm-forge/LICENSE.md new file mode 100644 index 000000000..0424fa020 --- /dev/null +++ b/evm-forge/LICENSE.md @@ -0,0 +1,16 @@ +MIT License + +Copyright (c) 2024 Paul Razvan Berg + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/evm-forge/README.md b/evm-forge/README.md new file mode 100644 index 000000000..592094343 --- /dev/null +++ b/evm-forge/README.md @@ -0,0 +1,240 @@ +# nibiru/evm-forge + +## What's Inside + +- [Forge](https://github.com/foundry-rs/foundry/blob/master/forge): compile, test, fuzz, format, and deploy smart + contracts +- [Forge Std](https://github.com/foundry-rs/forge-std): collection of helpful contracts and utilities for testing +- [Prettier](https://github.com/prettier/prettier): code formatter for non-Solidity files +- [Solhint](https://github.com/protofire/solhint): linter for Solidity code + +## Hacking: Getting Started + +Note that the shell instructions assume you're using Unix (e.g. MacOS or Ubuntu). + +Foundryup is the official installer for the Foundry toolchain. + +```bash +curl -L https://foundry.paradigm.xyz | bash +foundryup +bun install +forge install # Update ./lib +forge test # sanity check +``` + +Option 2: Build from source with `cargo` + +```bash +rustup update # For up-to-date rustc version +cargo install --git https://github.com/gakonst/foundry forge --bins --locked +bun install +forge install # Update ./lib +forge test # sanity check +``` + +[Foundry Book - Installation Options](https://book.getfoundry.sh/getting-started/installation) + +## Credits and Related Efforts + +This Foundry template is an adaptation of +[PaulRBerg's foundry template](https://github.com/PaulRBerg/foundry-template?tab=readme-ov-file) + +## + +# Foundry Template [![Open in Gitpod][gitpod-badge]][gitpod] [![Github Actions][gha-badge]][gha] [![Foundry][foundry-badge]][foundry] [![License: MIT][license-badge]][license] + +[gitpod]: https://gitpod.io/#https://github.com/PaulRBerg/foundry-template +[gitpod-badge]: https://img.shields.io/badge/Gitpod-Open%20in%20Gitpod-FFB45B?logo=gitpod +[gha]: https://github.com/PaulRBerg/foundry-template/actions +[gha-badge]: https://github.com/PaulRBerg/foundry-template/actions/workflows/ci.yml/badge.svg +[foundry]: https://getfoundry.sh/ +[foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg +[license]: https://opensource.org/licenses/MIT +[license-badge]: https://img.shields.io/badge/License-MIT-blue.svg + +A Foundry-based template for developing Solidity smart contracts, with sensible defaults. + +## Getting Started + +Click the [`Use this template`](https://github.com/PaulRBerg/foundry-template/generate) button at the top of the page to +create a new repository with this repo as the initial state. + +Or, if you prefer to install the template manually: + +```bash +mkdir my-project +cd my-project +forge init --template PaulRBerg/foundry-template +bun install # install Solhint, Prettier, and other Node.js deps +``` + +If this is your first time with Foundry, check out the +[installation](https://github.com/foundry-rs/foundry#installation) instructions. + +## Features + +This template builds upon the frameworks and libraries mentioned above, so please consult their respective documentation +for details about their specific features. + +For example, if you're interested in exploring Foundry in more detail, you should look at the +[Foundry Book](https://book.getfoundry.sh/). In particular, you may be interested in reading the +[Writing Tests](https://book.getfoundry.sh/forge/writing-tests.html) tutorial. + +### Sensible Defaults + +This template comes with a set of sensible default configurations for you to use. These defaults can be found in the +following files: + +```text +├── .editorconfig +├── .gitignore +├── .prettierignore +├── .prettierrc.yml +├── .solhint.json +├── foundry.toml +└── remappings.txt +``` + +### VSCode Integration + +This template is IDE agnostic, but for the best user experience, you may want to use it in VSCode alongside Nomic +Foundation's [Solidity extension](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity). + +For guidance on how to integrate a Foundry project in VSCode, please refer to this +[guide](https://book.getfoundry.sh/config/vscode). + +### GitHub Actions + +This template comes with GitHub Actions pre-configured. Your contracts will be linted and tested on every push and pull +request made to the `main` branch. + +You can edit the CI script in [.github/workflows/ci.yml](./.github/workflows/ci.yml). + +## Installing Dependencies + +Foundry typically uses git submodules to manage dependencies, but this template uses Node.js packages because +[submodules don't scale](https://twitter.com/PaulRBerg/status/1736695487057531328). + +This is how to install dependencies: + +1. Install the dependency using your preferred package manager, e.g. `bun install dependency-name` + - Use this syntax to install from GitHub: `bun install github:username/repo-name` +2. Add a remapping for the dependency in [remappings.txt](./remappings.txt), e.g. + `dependency-name=node_modules/dependency-name` + +Note that OpenZeppelin Contracts is pre-installed, so you can follow that as an example. + +## Writing Tests + +To write a new test contract, you start by importing `Test` from `forge-std`, and then you inherit it in your test +contract. Forge Std comes with a pre-instantiated [cheatcodes](https://book.getfoundry.sh/cheatcodes/) environment +accessible via the `vm` property. If you would like to view the logs in the terminal output, you can add the `-vvv` flag +and use [console.log](https://book.getfoundry.sh/faq?highlight=console.log#how-do-i-use-consolelog). + +This template comes with an example test contract [Foo.t.sol](./test/Foo.t.sol) + +## Usage + +This is a list of the most frequently needed commands. + +### Build + +Build the contracts: + +```bash +forge build +``` + +### Clean + +Delete the build artifacts and cache directories: + +```bash +forge clean +``` + +### Compile + +Compile the contracts: + +```bash +forge build +``` + +### Coverage + +Get a test coverage report: + +```bash +forge coverage +``` + +### Deploy + +Deploy to Anvil: + +```bash +forge script script/Deploy.s.sol --broadcast --fork-url http://localhost:8545 +``` + +For this script to work, you need to have a `MNEMONIC` environment variable set to a valid +[BIP39 mnemonic](https://iancoleman.io/bip39/). + +For instructions on how to deploy to a testnet or mainnet, check out the +[Solidity Scripting](https://book.getfoundry.sh/tutorials/solidity-scripting.html) tutorial. + +### Format + +Format the contracts: + +```bash +forge fmt +``` + +### Gas Usage + +Get a gas report: + +```bash +forge test --gas-report +``` + +### Lint + +Lint the contracts: + +```bash +bun run lint +``` + +### Test + +Run the tests: + +```bash +forge test +``` + +Generate test coverage and output result to the terminal: + +```bash +bun run test:coverage +``` + +Generate test coverage with lcov report (you'll have to open the `./coverage/index.html` file in your browser, to do so +simply copy paste the path): + +```bash +bun run test:coverage:report +``` + +## Related Efforts + +- [abigger87/femplate](https://github.com/abigger87/femplate) +- [cleanunicorn/ethereum-smartcontract-template](https://github.com/cleanunicorn/ethereum-smartcontract-template) +- [foundry-rs/forge-template](https://github.com/foundry-rs/forge-template) +- [FrankieIsLost/forge-template](https://github.com/FrankieIsLost/forge-template) + +## License + +The "NibiruChain/Nibiru/evm-forge" project is licensed under MIT. diff --git a/evm-forge/bun.lockb b/evm-forge/bun.lockb new file mode 100755 index 000000000..80826777b Binary files /dev/null and b/evm-forge/bun.lockb differ diff --git a/evm-forge/foundry.toml b/evm-forge/foundry.toml new file mode 100644 index 000000000..dacd6da68 --- /dev/null +++ b/evm-forge/foundry.toml @@ -0,0 +1,55 @@ +# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config + +[profile.default] + auto_detect_solc = false + block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT + bytecode_hash = "none" + evm_version = "shanghai" + fuzz = { runs = 1_000 } + gas_reports = ["*"] + optimizer = true + optimizer_runs = 10_000 + out = "out" + script = "script" + solc = "0.8.25" + src = "src" + test = "test" + +[profile.ci] + fuzz = { runs = 10_000 } + verbosity = 4 + +[etherscan] + arbitrum = { key = "${API_KEY_ARBISCAN}" } + avalanche = { key = "${API_KEY_SNOWTRACE}" } + base = { key = "${API_KEY_BASESCAN}" } + bnb_smart_chain = { key = "${API_KEY_BSCSCAN}" } + gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" } + goerli = { key = "${API_KEY_ETHERSCAN}" } + mainnet = { key = "${API_KEY_ETHERSCAN}" } + optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" } + polygon = { key = "${API_KEY_POLYGONSCAN}" } + sepolia = { key = "${API_KEY_ETHERSCAN}" } + +[fmt] + bracket_spacing = true + int_types = "long" + line_length = 120 + multiline_func_header = "all" + number_underscore = "thousands" + quote_style = "double" + tab_width = 4 + wrap_comments = true + +[rpc_endpoints] + arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}" + avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}" + base = "https://mainnet.base.org" + bnb_smart_chain = "https://bsc-dataseed.binance.org" + gnosis_chain = "https://rpc.gnosischain.com" + goerli = "https://goerli.infura.io/v3/${API_KEY_INFURA}" + localhost = "http://localhost:8545" + mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" + optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}" + polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}" + sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}" diff --git a/evm-forge/justfile b/evm-forge/justfile new file mode 100644 index 000000000..e79d4be3c --- /dev/null +++ b/evm-forge/justfile @@ -0,0 +1,64 @@ +# Displays available recipes by running `just -l`. +setup: + #!/usr/bin/env bash + just -l + +# Remove build artifacts and cache +clean: + rm -rf cache out + +# Build the contracts +build: + forge build + +# Run linters +lint: lint-sol prettier-check + +# Run Solidity linter +lint-sol: + forge fmt --check + bun solhint "{script,src,test}/**/*.sol" + +# Check formatting with Prettier +prettier-check: + prettier --check "**/*.{json,md,yml}" --ignore-path ".prettierignore" + +# Format files with Prettier +prettier-write: + prettier --write "**/*.{json,md,yml}" --ignore-path ".prettierignore" + +# Run tests +test: + forge test + +# Run all tests with verbose output +test-verbose: + forge test -vvv + +# Run test coverage +coverage: + forge coverage + +# Generate test coverage report +coverage-report: + forge coverage --report lcov + genhtml lcov.info --branch-coverage --output-dir coverage + +# Install dependencies +install: + bun install + forge install + +# Update dependencies +update: + bun update + forge update + +# Get gas report +gas-report: + forge test --gas-report + +# Format Solidity files +fmt: + forge fmt + just prettier-write diff --git a/evm-forge/package.json b/evm-forge/package.json new file mode 100644 index 000000000..c91238422 --- /dev/null +++ b/evm-forge/package.json @@ -0,0 +1,39 @@ +{ + "name": "@prb/foundry-template", + "description": "Foundry-based template for developing Solidity smart contracts", + "version": "1.0.0", + "author": { + "name": "Paul Razvan Berg", + "url": "https://github.com/PaulRBerg" + }, + "dependencies": { + "@openzeppelin/contracts": "^5.0.1" + }, + "devDependencies": { + "forge-std": "github:foundry-rs/forge-std#v1.8.1", + "prettier": "^3.3.3", + "prettier-plugin-solidity": "^1.4.1", + "solhint": "^3.6.2" + }, + "keywords": [ + "blockchain", + "ethereum", + "forge", + "foundry", + "smart-contracts", + "solidity", + "template" + ], + "private": true, + "scripts": { + "clean": "rm -rf cache out", + "build": "forge build", + "lint": "bun run lint:sol && bun run prettier:check", + "lint:sol": "forge fmt --check && bun solhint \"{script,src,test}/**/*.sol\"", + "prettier:check": "prettier --check \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "prettier:write": "prettier --write \"**/*.{json,md,yml}\" --ignore-path \".prettierignore\"", + "test": "forge test", + "test:coverage": "forge coverage", + "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage" + } +} diff --git a/evm-forge/remappings.txt b/evm-forge/remappings.txt new file mode 100644 index 000000000..550f90800 --- /dev/null +++ b/evm-forge/remappings.txt @@ -0,0 +1,2 @@ +@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ +forge-std/=node_modules/forge-std/ diff --git a/evm-forge/script/Base.s.sol b/evm-forge/script/Base.s.sol new file mode 100644 index 000000000..07135a212 --- /dev/null +++ b/evm-forge/script/Base.s.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.25 <0.9.0; + +import { Script } from "forge-std/src/Script.sol"; + +abstract contract BaseScript is Script { + /// @dev Included to enable compilation of the script without a $MNEMONIC environment variable. + string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk"; + + /// @dev Needed for the deterministic deployments. + bytes32 internal constant ZERO_SALT = bytes32(0); + + /// @dev The address of the transaction broadcaster. + address internal broadcaster; + + /// @dev Used to derive the broadcaster's address if $ETH_FROM is not defined. + string internal mnemonic; + + /// @dev Initializes the transaction broadcaster like this: + /// + /// - If $ETH_FROM is defined, use it. + /// - Otherwise, derive the broadcaster address from $MNEMONIC. + /// - If $MNEMONIC is not defined, default to a test mnemonic. + /// + /// The use case for $ETH_FROM is to specify the broadcaster key and its address via the command line. + constructor() { + address from = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) }); + if (from != address(0)) { + broadcaster = from; + } else { + mnemonic = vm.envOr({ name: "MNEMONIC", defaultValue: TEST_MNEMONIC }); + (broadcaster,) = deriveRememberKey({ mnemonic: mnemonic, index: 0 }); + } + } + + modifier broadcast() { + vm.startBroadcast(broadcaster); + _; + vm.stopBroadcast(); + } +} diff --git a/evm-forge/script/Deploy.s.sol b/evm-forge/script/Deploy.s.sol new file mode 100644 index 000000000..498db52e5 --- /dev/null +++ b/evm-forge/script/Deploy.s.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.25 <0.9.0; + +import { Foo } from "../src/Foo.sol"; + +import { BaseScript } from "./Base.s.sol"; + +/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting +contract Deploy is BaseScript { + function run() public broadcast returns (Foo foo) { + foo = new Foo(); + } +} diff --git a/evm-forge/src/Foo.sol b/evm-forge/src/Foo.sol new file mode 100644 index 000000000..74830704f --- /dev/null +++ b/evm-forge/src/Foo.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.25; + +contract Foo { + function id(uint256 value) external pure returns (uint256) { + return value; + } +} diff --git a/evm-forge/test/Foo.t.sol b/evm-forge/test/Foo.t.sol new file mode 100644 index 000000000..727337a87 --- /dev/null +++ b/evm-forge/test/Foo.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.25 <0.9.0; + +import { Test } from "forge-std/src/Test.sol"; +import { console2 } from "forge-std/src/console2.sol"; + +import { Foo } from "../src/Foo.sol"; + +interface IERC20 { + function balanceOf(address account) external view returns (uint256); +} + +/// @dev If this is your first time with Forge, read this tutorial in the Foundry Book: +/// https://book.getfoundry.sh/forge/writing-tests +contract FooTest is Test { + Foo internal foo; + + /// @dev A function invoked before each test case is run. + function setUp() public virtual { + // Instantiate the contract-under-test. + foo = new Foo(); + } + + /// @dev Basic test. Run it with `forge test -vvv` to see the console log. + function test_Example() external view { + console2.log("Hello World"); + uint256 x = 42; + assertEq(foo.id(x), x, "value mismatch"); + } + + /// @dev Fuzz test that provides random values for an unsigned integer, but which rejects zero as an input. + /// If you need more sophisticated input validation, you should use the `bound` utility instead. + /// See https://twitter.com/PaulRBerg/status/1622558791685242880 + function testFuzz_Example(uint256 x) external view { + vm.assume(x != 0); // or x = bound(x, 1, 100) + assertEq(foo.id(x), x, "value mismatch"); + } + + /// @dev Fork test that runs against an Ethereum Mainnet fork. For this to work, you need to set `API_KEY_ALCHEMY` + /// in your environment You can get an API key for free at https://alchemy.com. + function testFork_Example() external { + // Silently pass this test if there is no API key. + string memory alchemyApiKey = vm.envOr("API_KEY_ALCHEMY", string("")); + if (bytes(alchemyApiKey).length == 0) { + return; + } + + // Otherwise, run the test against the mainnet fork. + vm.createSelectFork({ urlOrAlias: "mainnet", blockNumber: 16_428_000 }); + address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address holder = 0x7713974908Be4BEd47172370115e8b1219F4A5f0; + uint256 actualBalance = IERC20(usdc).balanceOf(holder); + uint256 expectedBalance = 196_307_713.810457e6; + assertEq(actualBalance, expectedBalance); + } +} diff --git a/go.mod b/go.mod index b0222f850..270b09c5a 100644 --- a/go.mod +++ b/go.mod @@ -1,63 +1,95 @@ -module github.com/NibiruChain/nibiru +module github.com/NibiruChain/nibiru/v2 go 1.21 require ( - cosmossdk.io/errors v1.0.1 - cosmossdk.io/math v1.3.0 github.com/CosmWasm/wasmd v0.44.0 - github.com/CosmWasm/wasmvm v1.5.0 + github.com/CosmWasm/wasmvm v1.5.5 + github.com/NibiruChain/collections v0.5.0 + + // Consenus Engine + github.com/cometbft/cometbft v0.37.5 + github.com/cometbft/cometbft-db v0.11.0 + + // Cosmos-SDK and IBC + github.com/cosmos/cosmos-proto v1.0.0-beta.5 + github.com/cosmos/cosmos-sdk v0.47.11 + github.com/cosmos/ibc-go/v7 v7.3.2 + github.com/ethereum/go-ethereum v1.10.17 +) + +require ( + cosmossdk.io/api v0.7.0 // indirect + cosmossdk.io/errors v1.0.1 + cosmossdk.io/math v1.4.0 + cosmossdk.io/simapp v0.0.0-20230608160436-666c345ad23d github.com/MakeNowJust/heredoc/v2 v2.0.1 - github.com/NibiruChain/collections v0.4.0 github.com/armon/go-metrics v0.4.1 - github.com/cometbft/cometbft v0.37.4 - github.com/cometbft/cometbft-db v0.11.0 - github.com/cosmos/cosmos-proto v1.0.0-beta.4 - github.com/cosmos/cosmos-sdk v0.47.10 + github.com/btcsuite/btcd v0.24.2 + github.com/btcsuite/btcd/btcutil v1.1.5 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogoproto v1.4.10 - github.com/cosmos/ibc-go/v7 v7.3.2 github.com/gogo/protobuf v1.3.3 github.com/golang/mock v1.6.0 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 github.com/google/gofuzz v1.2.0 - github.com/gorilla/mux v1.8.0 + github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 + github.com/holiman/uint256 v1.2.4 // indirect github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/client_golang v1.18.0 github.com/rakyll/statik v0.1.7 - github.com/spf13/cast v1.5.1 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cast v1.6.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.16.0 - github.com/stretchr/testify v1.8.4 - google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 - google.golang.org/grpc v1.60.1 - google.golang.org/protobuf v1.32.0 + github.com/spf13/viper v1.18.2 + github.com/stretchr/testify v1.9.0 + github.com/tidwall/gjson v1.17.0 + github.com/tidwall/sjson v1.2.5 + github.com/tyler-smith/go-bip39 v1.1.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe + google.golang.org/grpc v1.62.1 + google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v2 v2.4.0 ) require ( - cloud.google.com/go v0.111.0 // indirect + cosmossdk.io/collections v0.4.0 + cosmossdk.io/tools/rosetta v0.2.1 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc + github.com/gorilla/websocket v1.5.0 + github.com/rs/cors v1.8.3 + github.com/rs/zerolog v1.32.0 + github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 + golang.org/x/crypto v0.31.0 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/net v0.33.0 + golang.org/x/text v0.21.0 +) + +require ( + cloud.google.com/go v0.112.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect - cloud.google.com/go/storage v1.30.1 // indirect - cosmossdk.io/api v0.4.0 // indirect - cosmossdk.io/core v0.6.1 // indirect + cloud.google.com/go/storage v1.36.0 // indirect + cosmossdk.io/core v0.10.0 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect cosmossdk.io/log v1.3.1 // indirect - cosmossdk.io/tools/rosetta v0.2.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect - github.com/DataDog/zstd v1.5.2 // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/VictoriaMetrics/fastcache v1.6.0 // indirect github.com/aws/aws-sdk-go v1.44.203 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -71,6 +103,7 @@ require ( github.com/coinbase/rosetta-sdk-go v0.7.9 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.0.2 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.21.0-beta.1 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect @@ -78,7 +111,7 @@ require ( github.com/cosmos/rosetta-sdk-go v0.10.0 // indirect github.com/creachadair/taskgroup v0.4.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set v1.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect @@ -87,47 +120,54 @@ require ( github.com/docker/distribution v2.8.2+incompatible // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect - github.com/felixge/httpsnoop v1.0.2 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/edsrzf/mmap-go v1.0.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/getsentry/sentry-go v0.23.0 // indirect + github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.1 // indirect + github.com/go-stack/stack v1.8.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect - github.com/golang/glog v1.1.2 // indirect + github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter v1.7.1 // indirect + github.com/hashicorp/go-getter v1.7.5 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/huandu/skiplist v1.2.0 // indirect + github.com/huin/goupnp v1.0.3 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.7 // indirect @@ -137,64 +177,86 @@ require ( github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mtibben/percent v0.2.1 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/tsdb v0.7.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rjeczalik/notify v0.9.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect - github.com/rs/cors v1.8.3 // indirect - github.com/rs/zerolog v1.32.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.6.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tklauser/go-sysconf v0.3.5 // indirect + github.com/tklauser/numcpus v0.2.2 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.8 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect - golang.org/x/crypto v0.16.0 // indirect - golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/api v0.149.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/api v0.155.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect + google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.6 // indirect pgregory.net/rapid v1.1.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 +replace ( + cosmossdk.io/api => cosmossdk.io/api v0.3.1 -replace cosmossdk.io/api => cosmossdk.io/api v0.3.1 + github.com/CosmWasm/wasmd => github.com/NibiruChain/wasmd v0.44.0-nibiru + github.com/cosmos/cosmos-sdk => github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru.2 -replace github.com/cosmos/iavl => github.com/cosmos/iavl v0.20.0 + github.com/cosmos/iavl => github.com/cosmos/iavl v0.20.0 -// pin version! 126854af5e6d has issues with the store so that queries fail -replace github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + github.com/ethereum/go-ethereum => github.com/NibiruChain/go-ethereum v1.10.27-nibiru + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + + github.com/linxGnu/grocksdb => github.com/linxGnu/grocksdb v1.8.12 + + // pin version! 126854af5e6d has issues with the store so that queries fail + github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + + // stick with compatible version or x/exp in v0.47.x line + golang.org/x/exp => golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb +) diff --git a/go.sum b/go.sum index 656b36fd3..f4364ff51 100644 --- a/go.sum +++ b/go.sum @@ -4,7 +4,6 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -18,7 +17,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -34,8 +32,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM= -cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -173,12 +171,11 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -193,19 +190,22 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= -cosmossdk.io/core v0.6.1 h1:OBy7TI2W+/gyn2z40vVvruK3di+cAluinA6cybFbE7s= -cosmossdk.io/core v0.6.1/go.mod h1:g3MMBCBXtxbDWBURDVnJE7XML4BG5qENhs0gzkcpuFA= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.10.0 h1:NP28Ol9YyRODmZLJg2ko/mUl40hMegeMzhJnG+XPkcY= +cosmossdk.io/core v0.10.0/go.mod h1:MygXNld9DvMgYY4yE76DM/mdZpgfeyRjy6FPjEEehlY= cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= -cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= -cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= +cosmossdk.io/math v1.4.0 h1:XbgExXFnXmF/CccPPEto40gOO7FpWu9yWNAZPN3nkNQ= +cosmossdk.io/math v1.4.0/go.mod h1:O5PkD4apz2jZs4zqFdTr16e1dcaQCc5z6lkEnrrppuk= +cosmossdk.io/simapp v0.0.0-20230608160436-666c345ad23d h1:E/8y0oG3u9hBR8l4F9MtC0LdZIamPCUwUoLlrHrX86I= +cosmossdk.io/simapp v0.0.0-20230608160436-666c345ad23d/go.mod h1:xbjky3L3DJEylaho6gXplkrMvJ5sFgv+qNX+Nn47bzY= cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= @@ -215,38 +215,41 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= -github.com/CosmWasm/wasmd v0.44.0 h1:2sbcoCAvfjCs1O0SWt53xULKjkV06dbSFthEViIC6Zg= -github.com/CosmWasm/wasmd v0.44.0/go.mod h1:tDyYN050qUcdd7LOxGeo2e185sEShyO3nJGl2Cf59+k= -github.com/CosmWasm/wasmvm v1.5.0 h1:3hKeT9SfwfLhxTGKH3vXaKFzBz1yuvP8SlfwfQXbQfw= -github.com/CosmWasm/wasmvm v1.5.0/go.mod h1:fXB+m2gyh4v9839zlIXdMZGeLAxqUdYdFQqYsTha2hc= +github.com/CosmWasm/wasmvm v1.5.5 h1:XlZI3xO5iUhiBqMiyzsrWEfUtk5gcBMNYIdHnsTB+NI= +github.com/CosmWasm/wasmvm v1.5.5/go.mod h1:Q0bSEtlktzh7W2hhEaifrFp1Erx11ckQZmjq8FLCyys= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= -github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A= github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/NibiruChain/collections v0.4.0 h1:KNJj+CJyqOT/Q33kcVzT2uLYIiwhiFAeZMhGLPge5Og= -github.com/NibiruChain/collections v0.4.0/go.mod h1:tKTlBL+Cs1oJnS4tT9MIaFWr7BWsUXrc7KPzP1LxRBo= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/NibiruChain/collections v0.5.0 h1:33pXpVTe1PK/tfdZlAJF1JF7AdzGNARG+iL9G/z3X7k= +github.com/NibiruChain/collections v0.5.0/go.mod h1:43L6yjuF0BMre/mw4gqn/kUOZz1c2Y3huZ/RQfBFrOQ= +github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru.2 h1:HtLNrkp0HhgxtbpdNwsasOod4uUNFpuwYpyceFtbj3E= +github.com/NibiruChain/cosmos-sdk v0.47.11-nibiru.2/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= +github.com/NibiruChain/go-ethereum v1.10.27-nibiru h1:o6lRFt57izoYwzN5cG8tnnBtJcaO3X7MjjN7PGGNCFg= +github.com/NibiruChain/go-ethereum v1.10.27-nibiru/go.mod h1:kvvL3nDceUcB+1qGUBAsVf5dW23RBR77fqxgx2PGNrQ= +github.com/NibiruChain/wasmd v0.44.0-nibiru h1:b+stNdbMFsl0+o4KedXyF83qRnEpB/jCiTGZZgv2h2U= +github.com/NibiruChain/wasmd v0.44.0-nibiru/go.mod h1:inrbdsixQ0Kdu4mFUg1u7fn3XPOEkzqieGv0H/gR0ck= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= @@ -261,6 +264,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -303,16 +307,24 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2 github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs= -github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= -github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= +github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= -github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -338,6 +350,7 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -370,6 +383,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -389,8 +404,8 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE github.com/coinbase/kryptology v1.8.0/go.mod h1:RYXOAPdzOGUe3qlSFkMGn58i3xUA8hmxYHksuq+8ciI= github.com/coinbase/rosetta-sdk-go v0.7.9 h1:lqllBjMnazTjIqYrOGv8h8jxjg9+hJazIGZr9ZvoCcA= github.com/coinbase/rosetta-sdk-go v0.7.9/go.mod h1:0/knutI7XGVqXmmH4OQD8OckFrbQ8yMsUZTG7FXCR2M= -github.com/cometbft/cometbft v0.37.4 h1:xyvvEqlyfK8MgNIIKVJaMsuIp03wxOcFmVkT26+Ikpg= -github.com/cometbft/cometbft v0.37.4/go.mod h1:Cmg5Hp4sNpapm7j+x0xRyt2g0juQfmB752ous+pA0G8= +github.com/cometbft/cometbft v0.37.5 h1:/U/TlgMh4NdnXNo+YU9T2NMCWyhXNDF34Mx582jlvq0= +github.com/cometbft/cometbft v0.37.5/go.mod h1:QC+mU0lBhKn8r9qvmnq53Dmf3DWBt4VtkcKw2C81wxY= github.com/cometbft/cometbft-db v0.11.0 h1:M3Lscmpogx5NTbb1EGyGDaFRdsoLWrUWimFEyf7jej8= github.com/cometbft/cometbft-db v0.11.0/go.mod h1:GDPJAC/iFHNjmZZPN8V8C1yr/eyityhi2W1hz2MGKSc= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= @@ -409,10 +424,10 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= -github.com/cosmos/cosmos-proto v1.0.0-beta.4 h1:aEL7tU/rLOmxZQ9z4i7mzxcLbSCY48OdY7lIWTLG7oU= -github.com/cosmos/cosmos-proto v1.0.0-beta.4/go.mod h1:oeB+FyVzG3XrQJbJng0EnV8Vljfk9XvTIpGILNU/9Co= -github.com/cosmos/cosmos-sdk v0.47.10 h1:Wxf5yEN3jZbG4fftxAMKB6rpd8ME0mxuCVihpz65dt0= -github.com/cosmos/cosmos-sdk v0.47.10/go.mod h1:UWpgWkhcsBIATS68uUC0del7IiBN4hPv/vqg8Zz23uw= +github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= +github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= @@ -431,9 +446,12 @@ github.com/cosmos/ledger-cosmos-go v0.12.4 h1:drvWt+GJP7Aiw550yeb3ON/zsrgW0jgh5s github.com/cosmos/ledger-cosmos-go v0.12.4/go.mod h1:fjfVWRf++Xkygt9wzCsjEBdjcf7wiiY35fv3ctT+k4M= github.com/cosmos/rosetta-sdk-go v0.10.0 h1:E5RhTruuoA7KTIXUcMicL76cffyeoyvNybzUGSKFTcM= github.com/cosmos/rosetta-sdk-go v0.10.0/go.mod h1:SImAZkb96YbwvoRkzSMQB6noNJXFgWl/ENIznEoYQI4= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -444,8 +462,10 @@ github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnG github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= @@ -470,16 +490,14 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -491,6 +509,7 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -503,31 +522,35 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.17/go.mod h1:Lt5WzjM07XlXc95YzrhosmR4J9Ahd6X2wyEV2SvGhk0= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= -github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE= github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -539,9 +562,6 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -555,10 +575,11 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= @@ -575,6 +596,7 @@ github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QX github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -592,12 +614,13 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -632,8 +655,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -684,7 +707,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -698,8 +720,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= @@ -717,15 +739,15 @@ github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMd github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -740,6 +762,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -750,13 +774,14 @@ github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIv github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= -github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -787,15 +812,19 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.3-0.20220313090229-ca81a64b4204/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= +github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= +github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -816,6 +845,7 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -856,8 +886,8 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDKIzp7y4voR9CX/nvcfymLmg2UiOio= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= @@ -866,7 +896,6 @@ github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPR github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -922,12 +951,13 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= @@ -949,6 +979,7 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -957,7 +988,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= @@ -983,6 +1013,7 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -1021,8 +1052,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= @@ -1040,10 +1071,10 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -1051,16 +1082,16 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1069,16 +1100,17 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= @@ -1088,6 +1120,7 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqn github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= +github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -1101,10 +1134,16 @@ github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= @@ -1113,6 +1152,7 @@ github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KR github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -1125,29 +1165,30 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1155,8 +1196,9 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -1165,13 +1207,15 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= @@ -1180,11 +1224,20 @@ github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -1192,6 +1245,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -1202,8 +1257,11 @@ github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= +github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -1213,6 +1271,8 @@ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPyS github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1239,20 +1299,28 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -1278,31 +1346,16 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us= golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1315,20 +1368,19 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1367,22 +1419,18 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1395,8 +1443,12 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1422,8 +1474,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1438,8 +1490,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1452,7 +1505,6 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1464,7 +1516,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1501,7 +1552,6 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1510,7 +1560,6 @@ golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1519,7 +1568,6 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1543,19 +1591,26 @@ golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1567,14 +1622,19 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1582,7 +1642,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1591,9 +1650,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1609,7 +1666,6 @@ golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1630,7 +1686,6 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1638,8 +1693,10 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1704,8 +1761,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY= -google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= +google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA= +google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1757,10 +1814,8 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1827,12 +1882,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe h1:0poefMBYvYbs7g5UkjS6HcxBPaTRAmznle9jnxYoAI8= +google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1874,8 +1929,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1892,8 +1947,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1908,12 +1963,11 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1930,7 +1984,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/gosdk/README.md b/gosdk/README.md new file mode 100644 index 000000000..0b9e67583 --- /dev/null +++ b/gosdk/README.md @@ -0,0 +1,61 @@ +# Nibiru Go SDK - NibiruChain/nibiru/gosdk + +A Golang client for interacting with the Nibiru blockchain. + +The Nibiru Go SDK extends the core blockchain logic with extensions to build +external clients for the Nibiru blockchain and easily access its query and +transaction types. + +--- + +## Dev Notes - Nibiru Go SDK + +### Finalizing "v1" + +- [ ] Migrate to the [Nibiru repo](https://github.com/NibiruChain/nibiru) and archive this one. +- [ ] feat: add in transaction broadcasting + - [x] initialize keyring obj with mnemonic + - [x] initialize keyring obj with priv key + - [x] write a test that sends a bank transfer. + - [ ] write a test that submits a text gov proposal. +- [x] docs: for grpc.go +- [ ] docs: for clients.go + +### Usage Guides & Dev Ex + +- [ ] Create a quick start guide: Connecting to Nibiru, querying Nibiru, sending +transactions on Nibiru +- [ ] Write usage examples + - [ ] Creating an account and keyring + - [ ] Querying balanaces + - [ ] Broadcasting txs to transfer funds + - [ ] Querying Wasm smart contracts + - [ ] Broadcasting txs to transfer funds +- [x] impl Tendermint RPC client +- [x] refactor: DRY improvements on the QueryClient initialization +- [x] ci: Add go tests to CI +- [x] ci: Add code coverage to CI +- [x] ci: Add linting to CI + +### Feature Backlog + +- [ ] impl wallet abstraction for the keyring +- [ ] epic: storing transaction history storage + +### Question Brain-dump + +Q: Should gosdk run as a binary? + +No, or at least, not initially. Since the software required to operate a full +node has more cumbersome dependencies like RocksDB that involve C-Go and compled +build steps, we may benefit from splitting "start" command from the bulk of the +subcommands available on teh Nibiru CLI. This would make it much easier to have a +command line tool that builds on Linux, Windows, Mac. + +Q: Should there be a way to run queries with JSON-RPC 2 instead of GRPC? + +We [implemented this in +python](https://github.com/NibiruChain/py-sdk/tree/v0.21.12/nibiru/jsonrpc) +without too much trouble, and it's not taxing to maintain. If we're going to +prioritize adding APIs for the CometBFT JSON-RPC methods, it should be in the +[Nibiru TypeScript SDK](https://github.com/NibiruChain/ts-sdk) first. diff --git a/gosdk/broadcast.go b/gosdk/broadcast.go new file mode 100644 index 000000000..636225ad8 --- /dev/null +++ b/gosdk/broadcast.go @@ -0,0 +1,198 @@ +package gosdk + +import ( + "context" + + cmtrpc "github.com/cometbft/cometbft/rpc/client" + sdkclient "github.com/cosmos/cosmos-sdk/client" + sdkclienttx "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + sdktypestx "github.com/cosmos/cosmos-sdk/types/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "google.golang.org/grpc" + + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" +) + +func BroadcastMsgsWithSeq( + args BroadcastArgs, + from sdk.AccAddress, + seq uint64, + msgs ...sdk.Msg, +) (*sdk.TxResponse, error) { + broadcaster := args.Broadcaster + + info, err := args.kring.KeyByAddress(from) + if err != nil { + return nil, err + } + + txBuilder := args.txCfg.NewTxBuilder() + err = txBuilder.SetMsgs(msgs...) + if err != nil { + return nil, err + } + + bondDenom := denoms.NIBI + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(bondDenom, sdk.NewInt(1000)))) + txBuilder.SetGasLimit(uint64(2 * common.TO_MICRO)) + + nums, err := args.gosdk.GetAccountNumbers(from.String()) + if err != nil { + return nil, err + } + + var accRetriever sdkclient.AccountRetriever = authtypes.AccountRetriever{} + txFactory := sdkclienttx.Factory{}. + WithChainID(args.chainID). + WithKeybase(args.kring). + WithTxConfig(args.txCfg). + WithAccountRetriever(accRetriever). + WithAccountNumber(nums.Number). + WithSequence(seq) + + overwriteSig := true + err = sdkclienttx.Sign(txFactory, info.Name, txBuilder, overwriteSig) + if err != nil { + return nil, err + } + + txBytes, err := args.txCfg.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return nil, err + } + + return broadcaster.BroadcastTxSync(txBytes) +} + +func BroadcastMsgs( + args BroadcastArgs, + from sdk.AccAddress, + msgs ...sdk.Msg, +) (*sdk.TxResponse, error) { + nums, err := args.gosdk.GetAccountNumbers(from.String()) + if err != nil { + return nil, err + } + return BroadcastMsgsWithSeq(args, from, nums.Sequence, msgs...) +} + +type Broadcaster interface { + BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) +} + +var ( + _ Broadcaster = (*BroadcasterTmRpc)(nil) + _ Broadcaster = (*BroadcasterGrpc)(nil) +) + +type BroadcasterTmRpc struct { + RPC cmtrpc.Client +} + +func (b BroadcasterTmRpc) BroadcastTxSync( + txBytes []byte, +) (*sdk.TxResponse, error) { + respRaw, err := b.RPC.BroadcastTxSync(context.Background(), txBytes) + if err != nil { + return nil, err + } + + return sdk.NewResponseFormatBroadcastTx(respRaw), err +} + +type BroadcasterGrpc struct { + GRPC *grpc.ClientConn +} + +func (b BroadcasterGrpc) BroadcastTx( + txBytes []byte, mode sdktypestx.BroadcastMode, +) (*sdk.TxResponse, error) { + txClient := sdktypestx.NewServiceClient(b.GRPC) + respRaw, err := txClient.BroadcastTx( + context.Background(), &sdktypestx.BroadcastTxRequest{ + TxBytes: txBytes, + Mode: mode, + }) + return respRaw.TxResponse, err +} + +func (b BroadcasterGrpc) BroadcastTxSync( + txBytes []byte, +) (*sdk.TxResponse, error) { + return b.BroadcastTx(txBytes, sdktypestx.BroadcastMode_BROADCAST_MODE_SYNC) +} + +func (b BroadcasterGrpc) BroadcastTxAsync( + txBytes []byte, +) (*sdk.TxResponse, error) { + return b.BroadcastTx(txBytes, sdktypestx.BroadcastMode_BROADCAST_MODE_ASYNC) +} + +// func GetTxBytes() ([]byte, error) { +// return txBytes, err +// } + +type BroadcastArgs struct { + kring keyring.Keyring + txCfg sdkclient.TxConfig + gosdk NibiruSDK + // clientCtx sdkclient.Context // TODO: implement + Broadcaster Broadcaster + rpc cmtrpc.Client + chainID string +} + +func initBroadcastArgs( + nc *NibiruSDK, broadcaster Broadcaster, +) (args BroadcastArgs) { + txConfig := nc.EncCfg.TxConfig + return BroadcastArgs{ + kring: nc.Keyring, + txCfg: txConfig, + gosdk: *nc, + Broadcaster: broadcaster, + rpc: nc.CometRPC, + chainID: nc.ChainId, + } +} + +func (nc *NibiruSDK) BroadcastMsgs( + from sdk.AccAddress, + msgs ...sdk.Msg, +) (*sdk.TxResponse, error) { + broadcaster := BroadcasterTmRpc{RPC: nc.CometRPC} + args := initBroadcastArgs(nc, broadcaster) + return BroadcastMsgs(args, from, msgs...) +} + +func (nc *NibiruSDK) BroadcastMsgsWithSeq( + from sdk.AccAddress, + seq uint64, + msgs ...sdk.Msg, +) (*sdk.TxResponse, error) { + broadcaster := BroadcasterTmRpc{RPC: nc.CometRPC} + args := initBroadcastArgs(nc, broadcaster) + return BroadcastMsgsWithSeq(args, from, seq, msgs...) +} + +func (nc *NibiruSDK) BroadcastMsgsGrpc( + from sdk.AccAddress, + msgs ...sdk.Msg, +) (*sdk.TxResponse, error) { + broadcaster := BroadcasterGrpc{GRPC: nc.Querier.ClientConn} + args := initBroadcastArgs(nc, broadcaster) + return BroadcastMsgs(args, from, msgs...) +} + +func (nc *NibiruSDK) BroadcastMsgsGrpcWithSeq( + from sdk.AccAddress, + seq uint64, + msgs ...sdk.Msg, +) (*sdk.TxResponse, error) { + broadcaster := BroadcasterGrpc{GRPC: nc.Querier.ClientConn} + args := initBroadcastArgs(nc, broadcaster) + return BroadcastMsgsWithSeq(args, from, seq, msgs...) +} diff --git a/gosdk/clientctx.go b/gosdk/clientctx.go new file mode 100644 index 000000000..1cf81ec61 --- /dev/null +++ b/gosdk/clientctx.go @@ -0,0 +1,42 @@ +package gosdk + +// import ( +// "github.com/NibiruChain/nibiru/v2/app" +// sdkclient "github.com/cosmos/cosmos-sdk/client" +// authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +// ) + +// TODO: https://github.com/NibiruChain/nibiru/issues/1894 +// Make a way to instantiate a NibiruSDK from a (*cli.Network, *cli.Validator) + +// ClientCtx: Docs for args +// +// - tmCfgRootDir: /node0/simd +// - Validator.Dir: /node0 +// - Validator.ClientCtx.KeyringDir: /node0/simcli + +// func NewNibiruSDKFromClientCtx( +// clientCtx sdkclient.Context, grpcUrl, cometRpcUrl string, +// ) (gosdk NibiruSDK, err error) { +// grpcConn, err := GetGRPCConnection(grpcUrl, true, 5) +// if err != nil { +// return +// } +// cometRpc, err := NewRPCClient(cometRpcUrl, "/websocket") +// if err != nil { +// return +// } +// querier, err := NewQuerier(grpcConn) +// if err != nil { +// return +// } +// return NibiruSDK{ +// ChainId: clientCtx.ChainID, +// Keyring: clientCtx.Keyring, +// EncCfg: app.MakeEncodingConfig(), +// Querier: querier, +// CometRPC: cometRpc, +// AccountRetriever: authtypes.AccountRetriever{}, +// GrpcClient: grpcConn, +// }, err +// } diff --git a/gosdk/export_test.go b/gosdk/export_test.go new file mode 100644 index 000000000..d5361367a --- /dev/null +++ b/gosdk/export_test.go @@ -0,0 +1,84 @@ +package gosdk + +import ( + "testing" + + "google.golang.org/grpc" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" + + tmconfig "github.com/cometbft/cometbft/config" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" +) + +type Blockchain struct { + GrpcConn *grpc.ClientConn + Cfg *testnetwork.Config + Network *testnetwork.Network + Val *testnetwork.Validator +} + +func CreateBlockchain(t *testing.T) (nibiru Blockchain, err error) { + EnsureNibiruPrefix() + encConfig := app.MakeEncodingConfig() + genState := genesis.NewTestGenesisState(encConfig) + cliCfg := testnetwork.BuildNetworkConfig(genState) + cfg := &cliCfg + cfg.NumValidators = 1 + + network, err := testnetwork.New( + t, + t.TempDir(), + *cfg, + ) + if err != nil { + return nibiru, err + } + err = network.WaitForNextBlock() + if err != nil { + return nibiru, err + } + + val := network.Validators[0] + AbsorbServerConfig(cfg, &val.AppConfig.Config) + AbsorbTmConfig(cfg, val.Ctx.Config) + + grpcConn, err := ConnectGrpcToVal(val) + if err != nil { + return nibiru, err + } + return Blockchain{ + GrpcConn: grpcConn, + Cfg: cfg, + Network: network, + Val: val, + }, err +} + +func ConnectGrpcToVal(val *testnetwork.Validator) (*grpc.ClientConn, error) { + grpcUrl := val.AppConfig.GRPC.Address + return GetGRPCConnection( + grpcUrl, true, 5, + ) +} + +func AbsorbServerConfig( + cfg *testnetwork.Config, srvCfg *serverconfig.Config, +) *testnetwork.Config { + cfg.GRPCAddress = srvCfg.GRPC.Address + cfg.APIAddress = srvCfg.API.Address + return cfg +} + +func AbsorbTmConfig( + cfg *testnetwork.Config, tmCfg *tmconfig.Config, +) *testnetwork.Config { + cfg.RPCAddress = tmCfg.RPC.ListenAddress + return cfg +} + +func (chain *Blockchain) TmRpcEndpoint() string { + return chain.Val.RPCAddress +} diff --git a/gosdk/gosdk.go b/gosdk/gosdk.go new file mode 100644 index 000000000..e46f60cc6 --- /dev/null +++ b/gosdk/gosdk.go @@ -0,0 +1,117 @@ +package gosdk + +import ( + "context" + "encoding/hex" + + cmtrpcclient "github.com/cometbft/cometbft/rpc/client" + cmtcoretypes "github.com/cometbft/cometbft/rpc/core/types" + "google.golang.org/grpc" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/appconst" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + csdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +type NibiruSDK struct { + ChainId string + Keyring keyring.Keyring + EncCfg app.EncodingConfig + Querier Querier + CometRPC cmtrpcclient.Client + AccountRetriever authtypes.AccountRetriever + GrpcClient *grpc.ClientConn +} + +func NewNibiruSdk( + chainId string, + grpcConn *grpc.ClientConn, + rpcEndpt string, +) (NibiruSDK, error) { + EnsureNibiruPrefix() + encCfg := app.MakeEncodingConfig() + keyring := keyring.NewInMemory(encCfg.Codec) + queryClient, err := NewQuerier(grpcConn) + if err != nil { + return NibiruSDK{}, err + } + cometRpc, err := NewRPCClient(rpcEndpt, "/websocket") + if err != nil { + return NibiruSDK{}, err + } + return NibiruSDK{ + ChainId: chainId, + Keyring: keyring, + EncCfg: encCfg, + Querier: queryClient, + CometRPC: cometRpc, + AccountRetriever: authtypes.AccountRetriever{}, + GrpcClient: grpcConn, + }, err +} + +func EnsureNibiruPrefix() { + csdkConfig := csdk.GetConfig() + nibiruPrefix := appconst.AccountAddressPrefix + if csdkConfig.GetBech32AccountAddrPrefix() != nibiruPrefix { + app.SetPrefixes(nibiruPrefix) + } +} + +func (nc *NibiruSDK) TxByHash(txHashHex string) (*cmtcoretypes.ResultTx, error) { + goCtx := context.Background() + txHashBz, err := TxHashHexToBytes(txHashHex) + if err != nil { + return nil, err + } + prove := true + res, err := nc.CometRPC.Tx(goCtx, txHashBz, prove) + return res, err +} + +func TxHashHexToBytes(txHashHex string) ([]byte, error) { + return hex.DecodeString(txHashHex) +} + +func TxHashBytesToHex(txHashBz []byte) (txHashHex string) { + return hex.EncodeToString(txHashBz) +} + +type AccountNumbers struct { + Number uint64 + Sequence uint64 +} + +func GetAccountNumbers( + address string, + grpcConn *grpc.ClientConn, + encCfg app.EncodingConfig, +) (nums AccountNumbers, err error) { + queryClient := authtypes.NewQueryClient(grpcConn) + resp, err := queryClient.Account(context.Background(), &authtypes.QueryAccountRequest{ + Address: address, + }) + if err != nil { + return nums, err + } + + // register auth interface + var acc authtypes.AccountI + if err := encCfg.InterfaceRegistry.UnpackAny(resp.Account, &acc); err != nil { + return nums, err + } + + return AccountNumbers{ + Number: acc.GetAccountNumber(), + Sequence: acc.GetSequence(), + }, err +} + +func (nc *NibiruSDK) GetAccountNumbers( + address string, +) (nums AccountNumbers, err error) { + return GetAccountNumbers(address, nc.Querier.ClientConn, nc.EncCfg) +} diff --git a/gosdk/gosdk_test.go b/gosdk/gosdk_test.go new file mode 100644 index 000000000..df1e6d039 --- /dev/null +++ b/gosdk/gosdk_test.go @@ -0,0 +1,158 @@ +package gosdk_test + +import ( + "fmt" + "strconv" + "testing" + + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + + "github.com/NibiruChain/nibiru/v2/gosdk" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" +) + +// -------------------------------------------------- +// NibiruClientSuite +// -------------------------------------------------- + +var ( + _ suite.SetupAllSuite = (*TestSuite)(nil) + _ suite.TearDownAllSuite = (*TestSuite)(nil) +) + +type TestSuite struct { + suite.Suite + + nibiruSdk *gosdk.NibiruSDK + grpcConn *grpc.ClientConn + cfg *testnetwork.Config + network *testnetwork.Network + val *testnetwork.Validator +} + +func TestSuite_RunAll(t *testing.T) { + suite.Run(t, new(TestSuite)) +} + +func (s *TestSuite) RPCEndpoint() string { + return s.val.RPCAddress +} + +// SetupSuite implements the suite.SetupAllSuite interface. This function runs +// prior to all of the other tests in the suite. +func (s *TestSuite) SetupSuite() { + testutil.BeforeIntegrationSuite(s.T()) + + s.Run("DoTestGetGrpcConnection_NoNetwork", s.DoTestGetGrpcConnection_NoNetwork) + + nibiru, err := gosdk.CreateBlockchain(s.T()) + s.NoError(err) + s.network = nibiru.Network + s.cfg = nibiru.Cfg + s.val = nibiru.Val + s.grpcConn = nibiru.GrpcConn +} + +func ConnectGrpcToVal(val *testnetwork.Validator) (*grpc.ClientConn, error) { + grpcUrl := val.AppConfig.GRPC.Address + return gosdk.GetGRPCConnection( + grpcUrl, true, 5, + ) +} + +func (s *TestSuite) ConnectGrpc() { + grpcConn, err := ConnectGrpcToVal(s.val) + s.NoError(err) + s.NotNil(grpcConn) + s.grpcConn = grpcConn +} + +func (s *TestSuite) TestNewNibiruSdk() { + rpcEndpt := s.val.RPCAddress + nibiruSdk, err := gosdk.NewNibiruSdk(s.cfg.ChainID, s.grpcConn, rpcEndpt) + s.NoError(err) + s.nibiruSdk = &nibiruSdk + s.nibiruSdk.Keyring = s.val.ClientCtx.Keyring + + s.Run("DoTestBroadcastMsgs", func() { + s.DoTestBroadcastMsgs() + }) + s.Run("DoTestBroadcastMsgsGrpc", func() { + for t := 0; t < 4; t++ { + s.NoError(s.network.WaitForNextBlock()) + } + s.DoTestBroadcastMsgsGrpc() + }) + s.Run("DoTestNewQueryClient", func() { + _, err := gosdk.NewQuerier(s.grpcConn) + s.NoError(err) + }) +} + +// FIXME: Q: What is the node home for a local validator? +func (s *TestSuite) UsefulPrints() { + tmCfgRootDir := s.val.Ctx.Config.RootDir + fmt.Printf("tmCfgRootDir: %v\n", tmCfgRootDir) + fmt.Printf("s.val.Dir: %v\n", s.val.Dir) + fmt.Printf("s.val.ClientCtx.KeyringDir: %v\n", s.val.ClientCtx.KeyringDir) +} + +func (s *TestSuite) AssertTxResponseSuccess(txResp *sdk.TxResponse) (txHashHex string) { + s.NotNil(txResp) + s.EqualValues(txResp.Code, 0) + return txResp.TxHash +} + +func (s *TestSuite) msgSendVars() (from, to sdk.AccAddress, amt sdk.Coins, msgSend sdk.Msg) { + from = s.val.Address + to = testutil.AccAddress() + amt = sdk.NewCoins(sdk.NewInt64Coin(denoms.NIBI, 420)) + msgSend = banktypes.NewMsgSend(from, to, amt) + return from, to, amt, msgSend +} + +func (s *TestSuite) DoTestBroadcastMsgs() (txHashHex string) { + from, _, _, msgSend := s.msgSendVars() + txResp, err := s.nibiruSdk.BroadcastMsgs( + from, msgSend, + ) + s.NoError(err) + return s.AssertTxResponseSuccess(txResp) +} + +func (s *TestSuite) DoTestBroadcastMsgsGrpc() (txHashHex string) { + from, _, _, msgSend := s.msgSendVars() + txResp, err := s.nibiruSdk.BroadcastMsgsGrpc( + from, msgSend, + ) + s.NoError(err) + txHashHex = s.AssertTxResponseSuccess(txResp) + + base := 10 + var txRespCode string = strconv.FormatUint(uint64(txResp.Code), base) + s.EqualValuesf(txResp.Code, 0, + "code: %v\nraw log: %s", txRespCode, txResp.RawLog) + return txHashHex +} + +func (s *TestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s *TestSuite) DoTestGetGrpcConnection_NoNetwork() { + grpcConn, err := gosdk.GetGRPCConnection( + gosdk.DefaultNetworkInfo.GrpcEndpoint+"notendpoint", true, 2, + ) + s.Error(err) + s.Nil(grpcConn) + + _, err = gosdk.NewQuerier(grpcConn) + s.Error(err) +} diff --git a/gosdk/grpc.go b/gosdk/grpc.go new file mode 100644 index 000000000..84a6c9871 --- /dev/null +++ b/gosdk/grpc.go @@ -0,0 +1,44 @@ +package gosdk + +import ( + "context" + "crypto/tls" + "fmt" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" +) + +// GetGRPCConnection establishes a connection to a gRPC server using either +// secure (TLS) or insecure credentials. The function blocks until the connection +// is established or the specified timeout is reached. +func GetGRPCConnection( + grpcUrl string, grpcInsecure bool, timeoutSeconds int64, +) (*grpc.ClientConn, error) { + var creds credentials.TransportCredentials + if grpcInsecure { + creds = insecure.NewCredentials() + } else { + creds = credentials.NewTLS(&tls.Config{}) + } + + options := []grpc.DialOption{ + grpc.WithBlock(), + grpc.WithTransportCredentials(creds), + } + timeout := time.Duration(timeoutSeconds) * time.Second + ctx, cancel := context.WithTimeout( + context.Background(), timeout, + ) + defer cancel() + + conn, err := grpc.DialContext(ctx, grpcUrl, options...) + if err != nil { + return nil, fmt.Errorf( + "%w: Cannot connect to gRPC endpoint %s\n", err, grpcUrl) + } + + return conn, nil +} diff --git a/gosdk/keys.go b/gosdk/keys.go new file mode 100644 index 000000000..5c0f23abb --- /dev/null +++ b/gosdk/keys.go @@ -0,0 +1,56 @@ +package gosdk + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdktestutil "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/codec" + + "github.com/cosmos/cosmos-sdk/crypto/hd" +) + +func EncodingConfig() codec.EncodingConfig { return app.MakeEncodingConfig() } + +// NewKeyring: Creates an empty, in-memory keyring +func NewKeyring() keyring.Keyring { + return keyring.NewInMemory(EncodingConfig().Codec) +} + +// TODO: Is it necessary to add support for interacting with local file system +// keyring? +// import ( +// "bufio" +// "os" +// "path/filepath" +// ) +// func NewKeyringLocal(nodeDir string) (keyring.Keyring) { +// clientDir := filepath.Join(nodeDir, "keyring") +// var cdc codec.Codec = EncodingConfig.Marshaler +// buf := bufio.NewReader(os.Stdin) +// return keyring.New( +// sdk.KeyringServiceName(), +// keyring.BackendTest, +// clientDir, +// buf, +// cdc, +// ) +// } + +func AddSignerToKeyringSecp256k1( + kring keyring.Keyring, mnemonic string, keyName string, +) (sdk.AccAddress, error) { + algo := hd.Secp256k1 + overwrite := true + addr, secretMnem, err := sdktestutil.GenerateSaveCoinKey( + kring, keyName, mnemonic, overwrite, algo, + ) + if err != nil { + return nil, fmt.Errorf("%w : Failed Key Generation with mnemonic %s", err, secretMnem) + } + + return addr, err +} diff --git a/gosdk/keys_test.go b/gosdk/keys_test.go new file mode 100644 index 000000000..c8fb5d6c5 --- /dev/null +++ b/gosdk/keys_test.go @@ -0,0 +1,56 @@ +package gosdk_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/gosdk" +) + +const LOCALNET_VALIDATOR_MNEMONIC = "guard cream sadness conduct invite crumble clock pudding hole grit liar hotel maid produce squeeze return argue turtle know drive eight casino maze host" + +func TestCreateSigner(t *testing.T) { + testCases := []struct { + testName string + mnemonic string + wantAddr string + expectErr bool + }{ + { + testName: "bad input", + mnemonic: "not a mnemonic", + expectErr: true, + }, + { + testName: "good input (localnet genesis)", + mnemonic: LOCALNET_VALIDATOR_MNEMONIC, + wantAddr: "nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl", + expectErr: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + kring := gosdk.NewKeyring() + keyName := "" + + gotAddr, err := gosdk.AddSignerToKeyringSecp256k1(kring, tc.mnemonic, keyName) + + if tc.expectErr { + require.Error(t, err) + return + } + + assert.EqualValues(t, gotAddr.String(), tc.wantAddr) + require.NoError(t, err) + }) + } +} + +func TestKeyring(t *testing.T) { + require.NotPanics(t, func() { + _ = gosdk.NewKeyring() + }) +} diff --git a/gosdk/netinfo.go b/gosdk/netinfo.go new file mode 100644 index 000000000..91c7e0095 --- /dev/null +++ b/gosdk/netinfo.go @@ -0,0 +1,17 @@ +package gosdk + +type NetworkInfo struct { + GrpcEndpoint string + LcdEndpoint string + TmRpcEndpoint string + WebsocketEndpoint string + ChainID string +} + +var DefaultNetworkInfo = NetworkInfo{ + GrpcEndpoint: "localhost:9090", + LcdEndpoint: "http://localhost:1317", + TmRpcEndpoint: "http://localhost:26657", + WebsocketEndpoint: "ws://localhost:26657/websocket", + ChainID: "nibiru-localnet-0", +} diff --git a/gosdk/querier.go b/gosdk/querier.go new file mode 100644 index 000000000..1ce1522b3 --- /dev/null +++ b/gosdk/querier.go @@ -0,0 +1,52 @@ +package gosdk + +import ( + "errors" + + wasm "github.com/CosmWasm/wasmd/x/wasm/types" + "google.golang.org/grpc" + + devgas "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" + epochs "github.com/NibiruChain/nibiru/v2/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/evm" + inflation "github.com/NibiruChain/nibiru/v2/x/inflation/types" + xoracle "github.com/NibiruChain/nibiru/v2/x/oracle/types" + tokenfactory "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" +) + +type Querier struct { + ClientConn *grpc.ClientConn + + // Smart Contracts + EVM evm.QueryClient + Wasm wasm.QueryClient + + // Other Modules + Devgas devgas.QueryClient + Epoch epochs.QueryClient + Inflation inflation.QueryClient + Oracle xoracle.QueryClient + TokenFactory tokenfactory.QueryClient +} + +func NewQuerier( + grpcConn *grpc.ClientConn, +) (Querier, error) { + if grpcConn == nil { + return Querier{}, errors.New( + "cannot create NibiruQueryClient with nil grpc.ClientConn") + } + + return Querier{ + ClientConn: grpcConn, + + EVM: evm.NewQueryClient(grpcConn), + Wasm: wasm.NewQueryClient(grpcConn), + + Devgas: devgas.NewQueryClient(grpcConn), + Epoch: epochs.NewQueryClient(grpcConn), + Inflation: inflation.NewQueryClient(grpcConn), + Oracle: xoracle.NewQueryClient(grpcConn), + TokenFactory: tokenfactory.NewQueryClient(grpcConn), + }, nil +} diff --git a/gosdk/rpc.go b/gosdk/rpc.go new file mode 100644 index 000000000..62cc549e9 --- /dev/null +++ b/gosdk/rpc.go @@ -0,0 +1,18 @@ +package gosdk + +import ( + cmtrpc "github.com/cometbft/cometbft/rpc/client" + cmtrpchttp "github.com/cometbft/cometbft/rpc/client/http" +) + +var _ cmtrpc.Client = (*cmtrpchttp.HTTP)(nil) + +// NewRPCClient: A remote Comet-BFT RPC client. An error is returned on +// invalid remote. The function panics when remote is nil. +// +// Args: +// - rpcEndpt: endpoint in the form ://: +// - websocket: websocket path (which always seems to be "/websocket") +func NewRPCClient(rpcEndpt string, websocket string) (*cmtrpchttp.HTTP, error) { + return cmtrpchttp.New(rpcEndpt, websocket) +} diff --git a/justfile b/justfile index 9c4a02662..cf36ea473 100644 --- a/justfile +++ b/justfile @@ -16,17 +16,38 @@ install-clean: build: make build -alias b := build +# Cleans the Go cache, modcache, and testcashe +clean-cache: + go clean -cache -testcache -modcache -# Generate protobuf code (Golang) for Nibiru -proto-gen: +# Generate protobuf-based types in Golang +gen-proto: #!/usr/bin/env bash make proto-gen -alias proto := proto-gen +# Generate Solidity artifacts for x/evm/embeds +gen-embeds: + #!/usr/bin/env bash + source contrib/bashlib.sh + + embeds_dir="x/evm/embeds" + log_info "Begin to compile Solidity in $embeds_dir" + which_ok npm + log_info "Using system node version: $(npm exec -- node -v)" + + cd "$embeds_dir" || (log_error "path $embeds_dir not found" && exit) + npx hardhat compile + log_success "Compiled Solidity in $embeds_dir" + + go run "gen-abi/main.go" + log_success "Saved ABI JSON files to $embeds_dir/abi for npm publishing" + +# Generate the Nibiru Token Registry files +gen-token-registry: + go run token-registry/main/main.go -# Build protobuf types (Rust) -proto-rs: +# Generate protobuf-based types in Rust +gen-proto-rs: bash proto/buf.gen.rs.sh lint: @@ -39,10 +60,47 @@ lint: golangci-lint run --allow-parallel-runners --fix -# Runs a Nibiru local network. Ex: "just localnet --features perp spot" + +# Runs a Nibiru local network. Ex: "just localnet", "just localnet --features featureA" localnet *PASS_FLAGS: make localnet FLAGS="{{PASS_FLAGS}}" +# Runs a Nibiru local network without building and installing. "just localnet --no-build" +localnet-fast: + make localnet FLAGS="--no-build" + +# Clears the logs directory +log-clear: + #!/usr/bin/env bash + if [ -d "logs" ] && [ "$(ls -A logs)" ]; then + rm logs/* && echo "Logs cleared successfully." + elif [ ! -d "logs" ]; then + echo "Logs directory does not exist. Nothing to clear." + else + echo "Logs directory is already empty." + fi + +# Runs "just localnet" with logging (logs/localnet.txt) +log-localnet: + #!/usr/bin/env bash + mkdir -p logs + just localnet 2>&1 | tee -a logs/localnet.txt + +# Runs the EVM E2E test with logging (logs/e2e.txt) +log-e2e: + #!/usr/bin/env bash + just test-e2e 2>&1 | tee -a logs/e2e.txt + +# Runs the EVM E2E tests +test-e2e: + #!/usr/bin/env bash + source contrib/bashlib.sh + log_info "Make sure the localnet is running! (just localnet)" + + cd evm-e2e + just test + + # Test: "localnet.sh" script test-localnet: #!/usr/bin/env bash @@ -61,13 +119,17 @@ test-chaosnet: which_ok nibid bash contrib/scripts/chaosnet.sh +# Alias for "gen-proto" +proto-gen: + just gen-proto + # Stops any `nibid` processes, even if they're running in the background. stop: kill $(pgrep -x nibid) || true # Runs golang formatter (gofumpt) fmt: - gofumpt -w x app + gofumpt -w x app gosdk eth # Format and lint tidy: @@ -82,3 +144,15 @@ test-release: release-publish: make release + +# Run Go tests (short mode) +test-unit: + go test ./... -short + +# Run Go tests (short mode) + coverage +test-coverage-unit: + make test-coverage-unit + +# Run Go tests, including live network tests + coverage +test-coverage-integration: + make test-coverage-integration diff --git a/proto/buf.gen.rs.sh b/proto/buf.gen.rs.sh index 00351fa7c..214468189 100644 --- a/proto/buf.gen.rs.sh +++ b/proto/buf.gen.rs.sh @@ -84,7 +84,8 @@ buf_gen() { local proto_dir="$1" if [ ! buf ]; then - echo "Please install buf to generate protos." + echo "Please install buf to generate protos. Try using:" + echo "go install github.com/bufbuild/buf/cmd/buf@latest" exit 1 fi diff --git a/proto/buf.gen.ts.yaml b/proto/buf.gen.ts.yaml index ee8da1045..5172a788a 100644 --- a/proto/buf.gen.ts.yaml +++ b/proto/buf.gen.ts.yaml @@ -4,5 +4,5 @@ managed: enabled: true plugins: - plugin: buf.build/community/stephenh-ts-proto:v1.150.1 - opt: esModuleInterop=true,forceLong=long,useOptionals=messages - out: . \ No newline at end of file + opt: esModuleInterop=true,forceLong=long,useOptionals=messages,exportCommonSymbols=false,outputIndex=true + out: . diff --git a/proto/buf.yaml b/proto/buf.yaml index 1ab1ff5ff..8c7df280c 100644 --- a/proto/buf.yaml +++ b/proto/buf.yaml @@ -1,5 +1,6 @@ version: v1 -name: buf.build/NibiruChain/nibiru +# Corresponds to Buf Schema Registry repo: https://buf.build/nibiru-chain/nibiru +name: buf.build/nibiru-chain/nibiru deps: - buf.build/cosmos/cosmos-proto - buf.build/cosmos/cosmos-sdk:v0.47.3 @@ -22,4 +23,4 @@ lint: - PACKAGE_DIRECTORY_MATCH ignore: - - tendermint \ No newline at end of file + - tendermint diff --git a/proto/eth/evm/v1/events.proto b/proto/eth/evm/v1/events.proto new file mode 100644 index 000000000..a99b98ddd --- /dev/null +++ b/proto/eth/evm/v1/events.proto @@ -0,0 +1,77 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +syntax = "proto3"; +package eth.evm.v1; + +import "cosmos/base/v1beta1/coin.proto"; +import "gogoproto/gogo.proto"; +import "eth/evm/v1/evm.proto"; + +option go_package = "github.com/NibiruChain/nibiru/v2/x/evm"; + +// EventEthereumTx defines the event for an Ethereum transaction +message EventEthereumTx { + // amount + string amount = 1; + // eth_hash is the Ethereum hash of the transaction + string eth_hash = 2; + // index of the transaction in the block + string index = 3; + // gas_used is the amount of gas used by the transaction + string gas_used = 4; + // hash is the Tendermint hash of the transaction + string hash = 5; + // recipient of the transaction + string recipient = 6; + // vm_error contains a VM error should it occur + string vm_error = 7; +} + +// EventTxLog defines the event for an Ethereum transaction log +message EventTxLog { + // tx_logs is an array of transaction logs + repeated Log logs = 1 [ (gogoproto.nullable) = false ]; +} + +// EventBlockBloom defines an Ethereum block bloom filter event +message EventBlockBloom { + // bloom is the bloom filter of the block + string bloom = 1; +} + +// EventFunTokenCreated defines a fun token creation event. +message EventFunTokenCreated { + string bank_denom = 1; + string erc20_contract_address = 2; + string creator = 3; + bool is_made_from_coin = 4; +} + +// ConvertCoinToEvm defines sending fun token to erc20 event. +message EventConvertCoinToEvm { + string sender = 1; + string erc20_contract_address = 2; + string to_eth_addr = 3; + cosmos.base.v1beta1.Coin bank_coin = 4 [ + (gogoproto.moretags) = "yaml:\"bank_coin\"", + (gogoproto.nullable) = false + ]; +} + +// EventTransfer defines event for EVM transfer +message EventTransfer { + string sender = 1; + string recipient = 2; + string amount = 3; +} + +// EventContractDeployed defines event for EVM contract deployment +message EventContractDeployed { + string sender = 1; + string contract_addr = 2; +} + +// EventContractExecuted defines event for EVM contract execution +message EventContractExecuted { + string sender = 1; + string contract_addr = 2; +} diff --git a/proto/eth/evm/v1/evm.proto b/proto/eth/evm/v1/evm.proto new file mode 100644 index 000000000..db785891a --- /dev/null +++ b/proto/eth/evm/v1/evm.proto @@ -0,0 +1,148 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +syntax = "proto3"; +package eth.evm.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/NibiruChain/nibiru/v2/x/evm"; + +// FunToken is a fungible token mapping between a Bank Coin and a corresponding +// ERC-20 smart contract. Bank Coins here refer to tokens like NIBI, IBC +// coins (ICS-20), and token factory coins, which are each represented by the +// "Coin" type in Golang. +message FunToken { + // Hexadecimal address of the ERC20 token to which the `FunToken` maps + string erc20_addr = 1 [ + (gogoproto.customtype) = "github.com/NibiruChain/nibiru/v2/eth.EIP55Addr", + (gogoproto.nullable) = false + ]; + + // bank_denom: Coin denomination in the Bank Module. + string bank_denom = 2; + + // True if the `FunToken` mapping was created from an existing Bank Coin and + // the ERC-20 contract gets deployed by the module account. False if the + // mapping was created from an externally owned ERC-20 contract. + bool is_made_from_coin = 3; +} + +// Params defines the EVM module parameters +message Params { + option (gogoproto.equal) = true; + // DEPRECATED: evm_denom + reserved 1; + // DEPRECATED: enable_create + reserved 2; + // DEPRECATED: enable_call + reserved 3; + + // extra_eips defines the additional EIPs for the vm.Config + repeated int64 extra_eips = 4 [ + (gogoproto.customname) = "ExtraEIPs", + (gogoproto.moretags) = "yaml:\"extra_eips\"" + ]; + // DEPRECATED: chain_config + reserved 5; + // DEPRECATED: allow_unprotected_txs + reserved 6; + + // DEPRECATED: active_precompiles + // All precompiles present according to the VM are active. + reserved 7; + // evm_channels is the list of channel identifiers from EVM compatible chains + repeated string evm_channels = 8 [ (gogoproto.customname) = "EVMChannels" ]; + + // Fee deducted and burned when calling "CreateFunToken" in units of + // "evm_denom". + string create_funtoken_fee = 9 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; +} + +// State represents a single Storage key value pair item. +message State { + // key is the stored key + string key = 1; + // value is the stored value for the given key + string value = 2; +} + +// Log represents an protobuf compatible Ethereum Log that defines a contract +// log event. These events are generated by the LOG opcode and stored/indexed by +// the node. +// +// NOTE: address, topics and data are consensus fields. The rest of the fields +// are derived, i.e. filled in by the nodes, but not secured by consensus. +message Log { + // address of the contract that generated the event + string address = 1; + // topics is a list of topics provided by the contract. + repeated string topics = 2; + // data which is supplied by the contract, usually ABI-encoded + bytes data = 3; + + // block_number of the block in which the transaction was included + uint64 block_number = 4 [ (gogoproto.jsontag) = "blockNumber" ]; + // tx_hash is the transaction hash + string tx_hash = 5 [ (gogoproto.jsontag) = "transactionHash" ]; + // tx_index of the transaction in the block + uint64 tx_index = 6 [ (gogoproto.jsontag) = "transactionIndex" ]; + // block_hash of the block in which the transaction was included + string block_hash = 7 [ (gogoproto.jsontag) = "blockHash" ]; + // index of the log in the block + uint64 index = 8 [ (gogoproto.jsontag) = "logIndex" ]; + + // removed is true if this log was reverted due to a chain + // reorganisation. You must pay attention to this field if you receive logs + // through a filter query. + bool removed = 9; +} + +// AccessTuple is the element type of an access list. +message AccessTuple { + option (gogoproto.goproto_getters) = false; + + // address is a hex formatted ethereum address + string address = 1; + // storage_keys are hex formatted hashes of the storage keys + repeated string storage_keys = 2 [ (gogoproto.jsontag) = "storageKeys" ]; +} + +// TracerConfig stores additional tracer args. For geth it's only one attr: +// onlyTopCall +message TracerConfig { + bool only_top_call = 1 [ (gogoproto.jsontag) = "onlyTopCall" ]; +} + +// TraceConfig holds extra parameters to trace functions. +message TraceConfig { + // DEPRECATED: DisableMemory and DisableReturnData have been renamed to + // Enable*. + reserved 4, 7; + reserved "disable_memory", "disable_return_data"; + + // tracer is a custom javascript tracer + string tracer = 1; + // timeout overrides the default timeout of 5 seconds for JavaScript-based + // tracing calls + string timeout = 2; + // reexec defines the number of blocks the tracer is willing to go back + uint64 reexec = 3; + // disable_stack switches stack capture + bool disable_stack = 5 [ (gogoproto.jsontag) = "disableStack" ]; + // disable_storage switches storage capture + bool disable_storage = 6 [ (gogoproto.jsontag) = "disableStorage" ]; + // debug can be used to print output during capture end + bool debug = 8; + // limit defines the maximum length of output, but zero means unlimited + int32 limit = 9; + // DEPRECATED: chain_config + reserved 10; + // enable_memory switches memory capture + bool enable_memory = 11 [ (gogoproto.jsontag) = "enableMemory" ]; + // enable_return_data switches the capture of return data + bool enable_return_data = 12 [ (gogoproto.jsontag) = "enableReturnData" ]; + // tracer_config configures the tracer options + TracerConfig tracer_config = 13 [ (gogoproto.jsontag) = "tracerConfig" ]; +} diff --git a/proto/eth/evm/v1/genesis.proto b/proto/eth/evm/v1/genesis.proto new file mode 100644 index 000000000..c46771685 --- /dev/null +++ b/proto/eth/evm/v1/genesis.proto @@ -0,0 +1,32 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +syntax = "proto3"; +package eth.evm.v1; + +import "eth/evm/v1/evm.proto"; +import "gogoproto/gogo.proto"; + +option go_package = "github.com/NibiruChain/nibiru/v2/x/evm"; + +// GenesisState defines the evm module's genesis state. +message GenesisState { + // accounts is an array containing the ethereum genesis accounts. + repeated GenesisAccount accounts = 1 [(gogoproto.nullable) = false]; + + // params defines all the parameters of the module. + Params params = 2 [(gogoproto.nullable) = false]; + + // Fungible token mappings corresponding to ERC-20 smart contract tokens. + repeated eth.evm.v1.FunToken funtoken_mappings = 3 [(gogoproto.nullable) = false]; +} + +// GenesisAccount defines an account to be initialized in the genesis state. +// Its main difference between with Geth's GenesisAccount is that it uses a +// custom storage type and that it doesn't contain the private key field. +message GenesisAccount { + // address defines an ethereum hex formated address of an account + string address = 1; + // code defines the hex bytes of the account code. + string code = 2; + // storage defines the set of state key values for the account. + repeated State storage = 3 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "Storage"]; +} diff --git a/proto/eth/evm/v1/query.proto b/proto/eth/evm/v1/query.proto new file mode 100644 index 000000000..bef574508 --- /dev/null +++ b/proto/eth/evm/v1/query.proto @@ -0,0 +1,319 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +syntax = "proto3"; +package eth.evm.v1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "eth/evm/v1/evm.proto"; +import "eth/evm/v1/tx.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/NibiruChain/nibiru/v2/x/evm"; + +// Query defines the gRPC querier service. +service Query { + // EthAccount queries a Nibiru account using its EVM address or Bech32 Nibiru + // address. + rpc EthAccount(QueryEthAccountRequest) returns (QueryEthAccountResponse) { + option (google.api.http).get = "/nibiru/evm/v1/eth_account/{address}"; + } + + // ValidatorAccount queries an Ethereum account's from a validator consensus + // Address. + rpc ValidatorAccount(QueryValidatorAccountRequest) returns (QueryValidatorAccountResponse) { + option (google.api.http).get = "/nibiru/evm/v1/validator_account/{cons_address}"; + } + + // Balance queries the balance of a the EVM denomination for a single + // EthAccount. + rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse) { + option (google.api.http).get = "/nibiru/evm/v1/balances/{address}"; + } + + // Storage queries the balance of all coins for a single account. + rpc Storage(QueryStorageRequest) returns (QueryStorageResponse) { + option (google.api.http).get = "/nibiru/evm/v1/storage/{address}/{key}"; + } + + // Code queries the balance of all coins for a single account. + rpc Code(QueryCodeRequest) returns (QueryCodeResponse) { + option (google.api.http).get = "/nibiru/evm/v1/codes/{address}"; + } + + // Params queries the parameters of x/evm module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/nibiru/evm/v1/params"; + } + + // EthCall implements the `eth_call` rpc api + rpc EthCall(EthCallRequest) returns (MsgEthereumTxResponse) { + option (google.api.http).get = "/nibiru/evm/v1/eth_call"; + } + + // EstimateGas implements the `eth_estimateGas` rpc api + rpc EstimateGas(EthCallRequest) returns (EstimateGasResponse) { + option (google.api.http).get = "/nibiru/evm/v1/estimate_gas"; + } + + // TraceTx implements the `debug_traceTransaction` rpc api + rpc TraceTx(QueryTraceTxRequest) returns (QueryTraceTxResponse) { + option (google.api.http).get = "/nibiru/evm/v1/trace_tx"; + } + + // TraceBlock implements the `debug_traceBlockByNumber` and `debug_traceBlockByHash` rpc api + rpc TraceBlock(QueryTraceBlockRequest) returns (QueryTraceBlockResponse) { + option (google.api.http).get = "/nibiru/evm/v1/trace_block"; + } + + // TraceCall implements the `debug_traceCall` rpc api + rpc TraceCall(QueryTraceTxRequest) returns (QueryTraceTxResponse) { + option (google.api.http).get = "/nibiru/evm/v1/trace_call"; + } + + // BaseFee queries the base fee of the parent block of the current block, + // Similar to feemarket module's method + rpc BaseFee(QueryBaseFeeRequest) returns (QueryBaseFeeResponse) { + option (google.api.http).get = "/nibiru/evm/v1/base_fee"; + } + + rpc FunTokenMapping(QueryFunTokenMappingRequest) returns (QueryFunTokenMappingResponse) { + option (google.api.http).get = "/nibiru/evm/v1/funtoken/{token}"; + } +} + +// QueryEthAccountRequest is the request type for the Query/Account RPC method. +message QueryEthAccountRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the Ethereum hex address or nibi Bech32 address to query the account for. + string address = 1; +} + +// QueryEthAccountResponse is the response type for the Query/EthAccount RPC method. +message QueryEthAccountResponse { + // balance is the balance of unibi (micronibi). + string balance = 1; + // balance_wei is the balance of wei (attoether, where NIBI is ether). + string balance_wei = 2; + // code_hash is the hex-formatted code bytes from the EOA. + string code_hash = 3; + // nonce is the account's sequence number. + uint64 nonce = 4; + + // eth_address: The hexadecimal-encoded string representing the 20 byte address + // of a Nibiru EVM account. + string eth_address = 5; + + // bech32_address is the nibi-prefixed address of the account that can receive + // bank transfers ("cosmos.bank.v1beta1.MsgSend"). + string bech32_address = 6; +} + +// QueryValidatorAccountRequest is the request type for the +// Query/ValidatorAccount RPC method. +message QueryValidatorAccountRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // cons_address is the validator cons address to query the account for. + string cons_address = 1; +} + +// QueryValidatorAccountResponse is the response type for the +// Query/ValidatorAccount RPC method. +message QueryValidatorAccountResponse { + // account_address is the Nibiru address of the account in bech32 format. + string account_address = 1; + // sequence is the account's sequence number. + uint64 sequence = 2; + // account_number is the account number + uint64 account_number = 3; +} + +// QueryBalanceRequest is the request type for the Query/Balance RPC method. +message QueryBalanceRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the ethereum hex address to query the balance for. + string address = 1; +} + +// QueryBalanceResponse is the response type for the Query/Balance RPC method. +message QueryBalanceResponse { + // balance is the balance of the EVM denomination + string balance = 1; + // balance is the balance of the EVM denomination in units of wei. + string balance_wei = 2; +} + +// QueryStorageRequest is the request type for the Query/Storage RPC method. +message QueryStorageRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the ethereum hex address to query the storage state for. + string address = 1; + + // key defines the key of the storage state + string key = 2; +} + +// QueryStorageResponse is the response type for the Query/Storage RPC +// method. +message QueryStorageResponse { + // value defines the storage state value hash associated with the given key. + string value = 1; +} + +// QueryCodeRequest is the request type for the Query/Code RPC method. +message QueryCodeRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // address is the ethereum hex address to query the code for. + string address = 1; +} + +// QueryCodeResponse is the response type for the Query/Code RPC +// method. +message QueryCodeResponse { + // code represents the code bytes from an ethereum address. + bytes code = 1; +} + +// QueryTxLogsRequest is the request type for the Query/TxLogs RPC method. +message QueryTxLogsRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // hash is the ethereum transaction hex hash to query the logs for. + string hash = 1; + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 2; +} + +// QueryTxLogsResponse is the response type for the Query/TxLogs RPC method. +message QueryTxLogsResponse { + // logs represents the ethereum logs generated from the given transaction. + repeated Log logs = 1; + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} + +// QueryParamsRequest defines the request type for querying x/evm parameters. +message QueryParamsRequest {} + +// QueryParamsResponse defines the response type for querying x/evm parameters. +message QueryParamsResponse { + // params define the evm module parameters. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// EthCallRequest defines EthCall request +message EthCallRequest { + // args uses the same json format as the json rpc api. + bytes args = 1; + // gas_cap defines the default gas cap to be used + uint64 gas_cap = 2; + // proposer_address of the requested block in hex format + bytes proposer_address = 3 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.ConsAddress"]; + // chain_id is the eip155 chain id parsed from the requested block header + int64 chain_id = 4; +} + +// EstimateGasResponse defines EstimateGas response +message EstimateGasResponse { + // gas returns the estimated gas + uint64 gas = 1; +} + +// QueryTraceTxRequest defines TraceTx request +message QueryTraceTxRequest { + // msg is the MsgEthereumTx for the requested transaction + MsgEthereumTx msg = 1; + // tx_index is not necessary anymore + reserved 2; + reserved "tx_index"; + // trace_config holds extra parameters to trace functions. + eth.evm.v1.TraceConfig trace_config = 3; + // predecessors is an array of transactions included in the same block + // need to be replayed first to get correct context for tracing. + repeated eth.evm.v1.MsgEthereumTx predecessors = 4; + // block_number of requested transaction + int64 block_number = 5; + // block_hash of requested transaction + string block_hash = 6; + // block_time of requested transaction + google.protobuf.Timestamp block_time = 7 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + // proposer_address is the proposer of the requested block + bytes proposer_address = 8 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.ConsAddress"]; + // chain_id is the the eip155 chain id parsed from the requested block header + int64 chain_id = 9; + // block_max_gas of the block of the requested transaction + int64 block_max_gas = 10; +} + +// QueryTraceTxResponse defines TraceTx response +message QueryTraceTxResponse { + // data is the response serialized in bytes + bytes data = 1; +} + +// QueryTraceBlockRequest defines TraceTx request +message QueryTraceBlockRequest { + // txs is an array of messages in the block + repeated MsgEthereumTx txs = 1; + // trace_config holds extra parameters to trace functions. + TraceConfig trace_config = 3; + // block_number of the traced block + int64 block_number = 5; + // block_hash (hex) of the traced block + string block_hash = 6; + // block_time of the traced block + google.protobuf.Timestamp block_time = 7 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + // proposer_address is the address of the requested block + bytes proposer_address = 8 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.ConsAddress"]; + // chain_id is the eip155 chain id parsed from the requested block header + int64 chain_id = 9; + // block_max_gas of the traced block + int64 block_max_gas = 10; +} + +// QueryTraceBlockResponse defines TraceBlock response +message QueryTraceBlockResponse { + // data is the response serialized in bytes + bytes data = 1; +} + +// QueryBaseFeeRequest defines the request type for querying the EIP1559 base +// fee. +message QueryBaseFeeRequest {} + +// QueryBaseFeeResponse returns the EIP1559 base fee. +// See https://github.com/ethereum/EIPs/blob/ba6c342c23164072adb500c3136e3ae6eabff306/EIPS/eip-1559.md. +message QueryBaseFeeResponse { + // base_fee is the EIP1559 base fee in units of wei. + string base_fee = 1 [(gogoproto.customtype) = "cosmossdk.io/math.Int"]; + // base_fee is the EIP1559 base fee in units of micronibi ("unibi"). + string base_fee_unibi = 2 [(gogoproto.customtype) = "cosmossdk.io/math.Int"]; +} + +message QueryFunTokenMappingRequest { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // Either the hexadecimal-encoded ERC20 contract address or denomination of the + // Bank Coin. + string token = 1; +} + +message QueryFunTokenMappingResponse { + option (gogoproto.equal) = false; + option (gogoproto.goproto_getters) = false; + + // fun_token is a mapping between the Bank Coin and the ERC20 contract address + eth.evm.v1.FunToken fun_token = 1; +} diff --git a/proto/eth/evm/v1/tx.proto b/proto/eth/evm/v1/tx.proto new file mode 100644 index 000000000..a1896fe86 --- /dev/null +++ b/proto/eth/evm/v1/tx.proto @@ -0,0 +1,287 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +syntax = "proto3"; +package eth.evm.v1; + +import "cosmos/msg/v1/msg.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos_proto/cosmos.proto"; +import "eth/evm/v1/evm.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/NibiruChain/nibiru/v2/x/evm"; + +// Msg defines the evm Msg service. +service Msg { + // EthereumTx defines a method submitting Ethereum transactions. + rpc EthereumTx(MsgEthereumTx) returns (MsgEthereumTxResponse) { + option (google.api.http).post = "/nibiru/evm/v1/ethereum_tx"; + }; + // UpdateParams defined a governance operation for updating the x/evm module + // parameters. The authority is hard-coded to the x/gov module account + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); + + // CreateFunToken: Create a "FunToken" mapping. Either the ERC20 contract + // address can be given to create the mapping to a Bank Coin, or the + // denomination for a Bank Coin can be given to create the mapping to an + // ERC20. + rpc CreateFunToken(MsgCreateFunToken) returns (MsgCreateFunTokenResponse); + + // ConvertCoinToEvm: Sends a coin with a valid "FunToken" mapping to the + // given recipient address ("to_eth_addr") in the corresponding ERC20 + // representation. + rpc ConvertCoinToEvm(MsgConvertCoinToEvm) + returns (MsgConvertCoinToEvmResponse); +} + +// MsgEthereumTx encapsulates an Ethereum transaction as an SDK message. +message MsgEthereumTx { + option (gogoproto.goproto_getters) = false; + + // data is inner transaction data of the Ethereum transaction + google.protobuf.Any data = 1; + + // size is the encoded storage size of the transaction (DEPRECATED) + double size = 2 [ (gogoproto.jsontag) = "-" ]; + // hash of the transaction in hex format + string hash = 3 [ (gogoproto.moretags) = "rlp:\"-\"" ]; + // from is the ethereum signer address in hex format. This address value is + // checked against the address derived from the signature (V, R, S) using the + // secp256k1 elliptic curve + string from = 4; +} + +// LegacyTx is the transaction data of regular Ethereum transactions. +// +// Note that setting "evm.Params.AllowUnprotectedTxs" to false will cause all +// non-EIP155 signed transactions to fail, as they'll lack replay protection. +// +// LegacyTx is a custom implementation of "LegacyTx" from +// "github.com/ethereum/go-ethereum/core/types". +message LegacyTx { + option (gogoproto.goproto_getters) = false; + option (cosmos_proto.implements_interface) = "TxData"; + + // nonce corresponds to the account nonce (transaction sequence). + uint64 nonce = 1; + // gas_price defines the value for each gas unit + string gas_price = 2 [ (gogoproto.customtype) = "cosmossdk.io/math.Int" ]; + // gas defines the gas limit defined for the transaction. + uint64 gas = 3 [ (gogoproto.customname) = "GasLimit" ]; + // to is the hex formatted address of the recipient + string to = 4; + // value defines the unsigned integer value of the transaction amount. + string value = 5 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.customname) = "Amount" + ]; + // data is the data payload bytes of the transaction. + bytes data = 6; + + // v defines the recovery id as the "v" signature value from the elliptic + // curve digital signatute algorithm (ECDSA). It indicates which of two + // possible solutions should be used to reconstruct the public key from the + // signature. In Ethereum, "v" takes the value 27 or 28 for transactions that + // are not relay-protected. + bytes v = 7; + + // r defines the x-coordinate of a point on the elliptic curve in the elliptic + // curve digital signatute algorithm (ECDSA). It's crucial in ensuring + // uniqueness of the signature. + bytes r = 8; + + // s define the signature value derived from the private key, message hash, + // and the value of "r". It ensures that the signature is tied to both the + // message and the private key of the sender. + bytes s = 9; +} + +// AccessListTx is the data of EIP-2930 access list transactions. +// It is a custom implementation of "AccessListTx" from +// "github.com/ethereum/go-ethereum/core/types". +message AccessListTx { + option (gogoproto.goproto_getters) = false; + option (cosmos_proto.implements_interface) = "TxData"; + + // chain_id of the destination EVM chain + string chain_id = 1 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.customname) = "ChainID", + (gogoproto.jsontag) = "chainID" + ]; + // nonce corresponds to the account nonce (transaction sequence). + uint64 nonce = 2; + // gas_price defines the value for each gas unit + string gas_price = 3 [ (gogoproto.customtype) = "cosmossdk.io/math.Int" ]; + // gas defines the gas limit defined for the transaction. + uint64 gas = 4 [ (gogoproto.customname) = "GasLimit" ]; + // to is the recipient address in hex format + string to = 5; + // value defines the unsigned integer value of the transaction amount. + string value = 6 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.customname) = "Amount" + ]; + // data is the data payload bytes of the transaction. + bytes data = 7; + // accesses is an array of access tuples + repeated AccessTuple accesses = 8 [ + (gogoproto.castrepeated) = "AccessList", + (gogoproto.jsontag) = "accessList", + (gogoproto.nullable) = false + ]; + + // v defines the recovery id and "v" signature value from the elliptic curve + // digital signatute algorithm (ECDSA). It indicates which of two possible + // solutions should be used to reconstruct the public key from the signature. + // In Ethereum, "v" takes the value 27 or 28 for transactions that are not + // relay-protected. + bytes v = 9; + + // r defines the x-coordinate of a point on the elliptic curve in the elliptic + // curve digital signatute algorithm (ECDSA). It's crucial in ensuring + // uniqueness of the signature. + bytes r = 10; + + // s define the signature value derived from the private key, message hash, + // and the value of "r". It ensures that the signature is tied to both the + // message and the private key of the sender. + bytes s = 11; +} + +// DynamicFeeTx is the data of EIP-1559 dynamic fee transactions. It is a custom +// implementation of "DynamicFeeTx" from +// "github.com/ethereum/go-ethereum/core/types". +message DynamicFeeTx { + option (gogoproto.goproto_getters) = false; + option (cosmos_proto.implements_interface) = "TxData"; + + // chain_id of the destination EVM chain + string chain_id = 1 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.customname) = "ChainID", + (gogoproto.jsontag) = "chainID" + ]; + // nonce corresponds to the account nonce (transaction sequence). + uint64 nonce = 2; + // gas_tip_cap defines the max value for the gas tip + string gas_tip_cap = 3 [ (gogoproto.customtype) = "cosmossdk.io/math.Int" ]; + // gas_fee_cap defines the max value for the gas fee + string gas_fee_cap = 4 [ (gogoproto.customtype) = "cosmossdk.io/math.Int" ]; + // gas defines the gas limit defined for the transaction. + uint64 gas = 5 [ (gogoproto.customname) = "GasLimit" ]; + // to is the hex formatted address of the recipient + string to = 6; + // value defines the the transaction amount. + string value = 7 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.customname) = "Amount" + ]; + // data is the data payload bytes of the transaction. + bytes data = 8; + // accesses is an array of access tuples + repeated AccessTuple accesses = 9 [ + (gogoproto.castrepeated) = "AccessList", + (gogoproto.jsontag) = "accessList", + (gogoproto.nullable) = false + ]; + // v defines the recovery id and "v" signature value from the elliptic curve + // digital signatute algorithm (ECDSA). It indicates which of two possible + // solutions should be used to reconstruct the public key from the signature. + // In Ethereum, "v" takes the value 27 or 28 for transactions that are not + // relay-protected. + bytes v = 10; + + // r defines the x-coordinate of a point on the elliptic curve in the elliptic + // curve digital signatute algorithm (ECDSA). It's crucial in ensuring + // uniqueness of the signature. + bytes r = 11; + + // s define the signature value derived from the private key, message hash, + // and the value of "r". It ensures that the signature is tied to both the + // message and the private key of the sender. + bytes s = 12; +} + +// ExtensionOptionsEthereumTx is an extension option for ethereum transactions +message ExtensionOptionsEthereumTx { + option (gogoproto.goproto_getters) = false; +} + +// MsgEthereumTxResponse defines the Msg/EthereumTx response type. +message MsgEthereumTxResponse { + option (gogoproto.goproto_getters) = false; + + // hash of the ethereum transaction in hex format. This hash differs from the + // Tendermint sha256 hash of the transaction bytes. See + // https://github.com/tendermint/tendermint/issues/6539 for reference + string hash = 1; + // logs contains the transaction hash and the proto-compatible ethereum + // logs. + repeated Log logs = 2 [ (gogoproto.nullable) = false ]; + // ret is the returned data from evm function (result or data supplied with + // revert opcode) + bytes ret = 3; + // vm_error is the error returned by vm execution + string vm_error = 4; + // gas_used specifies how much gas was consumed by the transaction + uint64 gas_used = 5; +} + +// MsgUpdateParams defines a Msg for updating the x/evm module parameters. +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address of the governance account. + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // params defines the x/evm parameters to update. + // NOTE: All parameters must be supplied. + eth.evm.v1.Params params = 2 [ (gogoproto.nullable) = false ]; +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +message MsgUpdateParamsResponse {} + +// MsgCreateFunToken: Arguments to create a "FunToken" mapping. Either the ERC20 +// contract address can be given to create the mapping to a Bank Coin, or the +// denomination for a Bank Coin can be given to create the mapping to an ERC20. +message MsgCreateFunToken { + // Hexadecimal address of the ERC20 token to which the `FunToken` maps + string from_erc20 = 1 [ + (gogoproto.customtype) = "github.com/NibiruChain/nibiru/v2/eth.EIP55Addr", + (gogoproto.nullable) = true + ]; + + // Coin denomination in the Bank Module. + string from_bank_denom = 2; + + // Sender: Address for the signer of the transaction. + string sender = 3; +} + +message MsgCreateFunTokenResponse { + // Fungible token mapping corresponding to ERC20 tokens. + eth.evm.v1.FunToken funtoken_mapping = 1 [ (gogoproto.nullable) = false ]; +} + +// MsgConvertCoinToEvm: Arguments to send a Bank Coin to ERC-20 representation +message MsgConvertCoinToEvm { + // Hexadecimal address of the ERC20 token to which the `FunToken` maps + string to_eth_addr = 1 [ + (gogoproto.customtype) = "github.com/NibiruChain/nibiru/v2/eth.EIP55Addr", + (gogoproto.nullable) = false + ]; + + // Sender: Address for the signer of the transaction. + string sender = 2; + + // Bank Coin to get converted to ERC20 + cosmos.base.v1beta1.Coin bank_coin = 3 [ + (gogoproto.moretags) = "yaml:\"bank_coin\"", + (gogoproto.nullable) = false + ]; +} +message MsgConvertCoinToEvmResponse {} diff --git a/proto/eth/types/v1/account.proto b/proto/eth/types/v1/account.proto new file mode 100644 index 000000000..093614343 --- /dev/null +++ b/proto/eth/types/v1/account.proto @@ -0,0 +1,25 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +syntax = "proto3"; +package eth.types.v1; + +import "cosmos/auth/v1beta1/auth.proto"; +import "cosmos_proto/cosmos.proto"; +import "gogoproto/gogo.proto"; + +option go_package = "github.com/NibiruChain/nibiru/v2/eth"; + +// EthAccount implements the authtypes.AccountI interface and embeds an +// authtypes.BaseAccount type. It is compatible with the auth AccountKeeper. +message EthAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.equal) = false; + + option (cosmos_proto.implements_interface) = "github.com/cosmos/cosmos-sdk/x/auth/types.cosmos.auth.v1beta1.AccountI"; + + // base_account is an authtypes.BaseAccount + cosmos.auth.v1beta1.BaseAccount base_account = 1 + [(gogoproto.embed) = true, (gogoproto.moretags) = "yaml:\"base_account\""]; + + // code_hash is the hash calculated from the code contents + string code_hash = 2 [(gogoproto.moretags) = "yaml:\"code_hash\""]; +} diff --git a/proto/eth/types/v1/indexer.proto b/proto/eth/types/v1/indexer.proto new file mode 100644 index 000000000..df17c31f0 --- /dev/null +++ b/proto/eth/types/v1/indexer.proto @@ -0,0 +1,33 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +syntax = "proto3"; +package eth.types.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/NibiruChain/nibiru/v2/eth"; + +// TxResult is the value stored in eth tx indexer +message TxResult { + option (gogoproto.goproto_getters) = false; + + // height of the blockchain + int64 height = 1; + // tx_index is the index of the block transaction. It is not the index of an + // "internal transaction" + uint32 tx_index = 2; + // msg_index in a batch transaction + uint32 msg_index = 3; + + // eth_tx_index is the index in the list of valid eth tx in the block. Said + // another way, it is the index of the transaction list returned by + // eth_getBlock API. + int32 eth_tx_index = 4; + // failed is true if the eth transaction did not succeed + bool failed = 5; + // gas_used by the transaction. If it exceeds the block gas limit, + // it's set to gas limit, which is what's actually deducted by ante handler. + uint64 gas_used = 6; + // cumulative_gas_used specifies the cumulated amount of gas used for all + // processed messages within the current batch transaction. + uint64 cumulative_gas_used = 7; +} diff --git a/proto/nibiru/devgas/v1/devgas.proto b/proto/nibiru/devgas/v1/devgas.proto index f18ceca01..38711a7ee 100644 --- a/proto/nibiru/devgas/v1/devgas.proto +++ b/proto/nibiru/devgas/v1/devgas.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package nibiru.devgas.v1; -option go_package = "github.com/NibiruChain/nibiru/x/devgas/v1/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types"; // FeeShare defines an instance that organizes fee distribution conditions for // the owner of a given smart contract diff --git a/proto/nibiru/devgas/v1/event.proto b/proto/nibiru/devgas/v1/event.proto index 6d3f04cb5..10a67045b 100644 --- a/proto/nibiru/devgas/v1/event.proto +++ b/proto/nibiru/devgas/v1/event.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package nibiru.devgas.v1; -option go_package = "github.com/NibiruChain/nibiru/x/devgas/v1/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types"; // ABCI event emitted when a deployer registers a contract to receive fee // sharing payouts, specifying the deployer, contract, and withdrawer addresses. diff --git a/proto/nibiru/devgas/v1/genesis.proto b/proto/nibiru/devgas/v1/genesis.proto index ed28cb9bd..bd72093f5 100644 --- a/proto/nibiru/devgas/v1/genesis.proto +++ b/proto/nibiru/devgas/v1/genesis.proto @@ -3,7 +3,7 @@ package nibiru.devgas.v1; import "nibiru/devgas/v1/devgas.proto"; import "gogoproto/gogo.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/devgas/v1/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types"; // GenesisState defines the module's genesis state. message GenesisState { diff --git a/proto/nibiru/devgas/v1/query.proto b/proto/nibiru/devgas/v1/query.proto index a7bd65f96..c9a26c991 100644 --- a/proto/nibiru/devgas/v1/query.proto +++ b/proto/nibiru/devgas/v1/query.proto @@ -7,7 +7,7 @@ import "nibiru/devgas/v1/devgas.proto"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/devgas/v1/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types"; // Query defines the gRPC querier service. service Query { diff --git a/proto/nibiru/devgas/v1/tx.proto b/proto/nibiru/devgas/v1/tx.proto index ca72c9963..33f798a7c 100644 --- a/proto/nibiru/devgas/v1/tx.proto +++ b/proto/nibiru/devgas/v1/tx.proto @@ -8,7 +8,7 @@ import "cosmos/msg/v1/msg.proto"; import "cosmos_proto/cosmos.proto"; import "nibiru/devgas/v1/genesis.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/devgas/v1/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types"; // Msg defines the fees Msg service. service Msg { diff --git a/proto/nibiru/epochs/v1/event.proto b/proto/nibiru/epochs/v1/event.proto index bb481cab8..b61945c71 100644 --- a/proto/nibiru/epochs/v1/event.proto +++ b/proto/nibiru/epochs/v1/event.proto @@ -4,7 +4,7 @@ package nibiru.epochs.v1; import "gogoproto/gogo.proto"; import "google/protobuf/timestamp.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/epochs/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/epochs/types"; message EventEpochStart { // Epoch number, starting from 1. diff --git a/proto/nibiru/epochs/v1/genesis.proto b/proto/nibiru/epochs/v1/genesis.proto index 0ce843e10..83433100f 100644 --- a/proto/nibiru/epochs/v1/genesis.proto +++ b/proto/nibiru/epochs/v1/genesis.proto @@ -6,7 +6,7 @@ import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "nibiru/epochs/v1/state.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/epochs/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/epochs/types"; // GenesisState defines the epochs module's genesis state. message GenesisState { diff --git a/proto/nibiru/epochs/v1/query.proto b/proto/nibiru/epochs/v1/query.proto index 76ed2054a..f3e472101 100644 --- a/proto/nibiru/epochs/v1/query.proto +++ b/proto/nibiru/epochs/v1/query.proto @@ -6,7 +6,7 @@ import "google/api/annotations.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; import "nibiru/epochs/v1/state.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/epochs/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/epochs/types"; // Query defines the gRPC querier service. service Query { diff --git a/proto/nibiru/epochs/v1/state.proto b/proto/nibiru/epochs/v1/state.proto index c2395d33a..502130da9 100644 --- a/proto/nibiru/epochs/v1/state.proto +++ b/proto/nibiru/epochs/v1/state.proto @@ -5,7 +5,7 @@ import "gogoproto/gogo.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/epochs/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/epochs/types"; message EpochInfo { // A string identifier for the epoch. e.g. "15min" or "1hour" diff --git a/proto/nibiru/genmsg/v1/genmsg.proto b/proto/nibiru/genmsg/v1/genmsg.proto index 54d02d2b9..06f45c719 100644 --- a/proto/nibiru/genmsg/v1/genmsg.proto +++ b/proto/nibiru/genmsg/v1/genmsg.proto @@ -3,7 +3,7 @@ package nibiru.genmsg.v1; import "google/protobuf/any.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/genmsg/v1"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/genmsg/v1"; // GenesisState represents the messages to be processed during genesis by the genmsg module. message GenesisState { diff --git a/proto/nibiru/inflation/v1/event.proto b/proto/nibiru/inflation/v1/event.proto index ef321ee22..20a211e7f 100644 --- a/proto/nibiru/inflation/v1/event.proto +++ b/proto/nibiru/inflation/v1/event.proto @@ -4,7 +4,7 @@ package nibiru.inflation.v1; import "gogoproto/gogo.proto"; import "cosmos/base/v1beta1/coin.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/inflation/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/inflation/types"; // EventInflationDistribution: Emitted when NIBI tokens are minted on the // network based on Nibiru's inflation schedule. diff --git a/proto/nibiru/inflation/v1/genesis.proto b/proto/nibiru/inflation/v1/genesis.proto index 773bfaef5..fdea6bbf0 100644 --- a/proto/nibiru/inflation/v1/genesis.proto +++ b/proto/nibiru/inflation/v1/genesis.proto @@ -5,7 +5,7 @@ package nibiru.inflation.v1; import "gogoproto/gogo.proto"; import "nibiru/inflation/v1/inflation.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/inflation/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/inflation/types"; // GenesisState defines the inflation module's genesis state. message GenesisState { diff --git a/proto/nibiru/inflation/v1/inflation.proto b/proto/nibiru/inflation/v1/inflation.proto index cad921ede..44cda080c 100644 --- a/proto/nibiru/inflation/v1/inflation.proto +++ b/proto/nibiru/inflation/v1/inflation.proto @@ -3,7 +3,7 @@ package nibiru.inflation.v1; import "gogoproto/gogo.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/inflation/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/inflation/types"; // InflationDistribution defines the distribution in which inflation is // allocated through minting on each epoch (staking, community, strategic). It diff --git a/proto/nibiru/inflation/v1/query.proto b/proto/nibiru/inflation/v1/query.proto new file mode 100644 index 000000000..ebf2a5c1e --- /dev/null +++ b/proto/nibiru/inflation/v1/query.proto @@ -0,0 +1,119 @@ +syntax = "proto3"; +package nibiru.inflation.v1; + +import "cosmos/base/v1beta1/coin.proto"; +import "nibiru/inflation/v1/genesis.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/NibiruChain/nibiru/v2/x/inflation/types"; + +// Query provides defines the gRPC querier service. +service Query { + // Period retrieves current period. + rpc Period(QueryPeriodRequest) returns (QueryPeriodResponse) { + option (google.api.http).get = "/nibiru/inflation/v1/period"; + } + + // EpochMintProvision retrieves current minting epoch provision value. + rpc EpochMintProvision(QueryEpochMintProvisionRequest) + returns (QueryEpochMintProvisionResponse) { + option (google.api.http).get = "/nibiru/inflation/v1/epoch_mint_provision"; + } + + // SkippedEpochs retrieves the total number of skipped epochs. + rpc SkippedEpochs(QuerySkippedEpochsRequest) + returns (QuerySkippedEpochsResponse) { + option (google.api.http).get = "/nibiru/inflation/v1/skipped_epochs"; + } + + // CirculatingSupply retrieves the total number of tokens that are in + // circulation (i.e. excluding unvested tokens). + rpc CirculatingSupply(QueryCirculatingSupplyRequest) + returns (QueryCirculatingSupplyResponse) { + option (google.api.http).get = "/nibiru/inflation/v1/circulating_supply"; + } + + // InflationRate retrieves the inflation rate of the current period. + rpc InflationRate(QueryInflationRateRequest) + returns (QueryInflationRateResponse) { + option (google.api.http).get = "/nibiru/inflation/v1/inflation_rate"; + } + + // Params retrieves the total set of minting parameters. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/nibiru/inflation/v1/params"; + } +} + +// QueryPeriodRequest is the request type for the Query/Period RPC method. +message QueryPeriodRequest {} + +// QueryPeriodResponse is the response type for the Query/Period RPC method. +message QueryPeriodResponse { + // period is the current minting per epoch provision value. + uint64 period = 1; +} + +// QueryEpochMintProvisionRequest is the request type for the +// Query/EpochMintProvision RPC method. +message QueryEpochMintProvisionRequest {} + +// QueryEpochMintProvisionResponse is the response type for the +// Query/EpochMintProvision RPC method. +message QueryEpochMintProvisionResponse { + // epoch_mint_provision is the current minting per epoch provision value. + cosmos.base.v1beta1.DecCoin epoch_mint_provision = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins" + ]; +} + +// QuerySkippedEpochsRequest is the request type for the Query/SkippedEpochs RPC +// method. +message QuerySkippedEpochsRequest {} + +// QuerySkippedEpochsResponse is the response type for the Query/SkippedEpochs +// RPC method. +message QuerySkippedEpochsResponse { + // skipped_epochs is the number of epochs that the inflation module has been + // disabled. + uint64 skipped_epochs = 1; +} + +// QueryCirculatingSupplyRequest is the request type for the +// Query/CirculatingSupply RPC method. +message QueryCirculatingSupplyRequest {} + +// QueryCirculatingSupplyResponse is the response type for the +// Query/CirculatingSupply RPC method. +message QueryCirculatingSupplyResponse { + // circulating_supply is the total amount of coins in circulation + cosmos.base.v1beta1.DecCoin circulating_supply = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins" + ]; +} + +// QueryInflationRateRequest is the request type for the Query/InflationRate RPC +// method. +message QueryInflationRateRequest {} + +// QueryInflationRateResponse is the response type for the Query/InflationRate +// RPC method. +message QueryInflationRateResponse { + // inflation_rate by which the total supply increases within one period + string inflation_rate = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + nibiru.inflation.v1.Params params = 1 [ (gogoproto.nullable) = false ]; +} diff --git a/proto/nibiru/inflation/v1/tx.proto b/proto/nibiru/inflation/v1/tx.proto index ce516b422..829b97775 100644 --- a/proto/nibiru/inflation/v1/tx.proto +++ b/proto/nibiru/inflation/v1/tx.proto @@ -6,7 +6,7 @@ import "google/api/annotations.proto"; import "nibiru/inflation/v1/inflation.proto"; import "cosmos/base/v1beta1/coin.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/inflation/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/inflation/types"; service Msg { // ToggleInflation defines a method to enable or disable inflation. @@ -59,4 +59,13 @@ message MsgEditInflationParams { message MsgToggleInflationResponse {} -message MsgEditInflationParamsResponse {} \ No newline at end of file +message MsgEditInflationParamsResponse {} + +// MsgBurn: allows burning of any token +message MsgBurn { + string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; + cosmos.base.v1beta1.Coin coin = 2 + [ (gogoproto.moretags) = "yaml:\"coin\"", (gogoproto.nullable) = false ]; +} + +message MsgBurnResponse {} diff --git a/proto/nibiru/oracle/v1/event.proto b/proto/nibiru/oracle/v1/event.proto index f9dda8853..24106088e 100644 --- a/proto/nibiru/oracle/v1/event.proto +++ b/proto/nibiru/oracle/v1/event.proto @@ -6,7 +6,7 @@ import "nibiru/oracle/v1/oracle.proto"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/oracle/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/oracle/types"; // Emitted when a price is posted message EventPriceUpdate { @@ -53,3 +53,25 @@ message EventAggregatePrevote { // transaction messages on behalf of the voting validator. string feeder = 2; } + + +message EventValidatorPerformance{ + // Validator is the Bech32 address to which the vote will be credited. + string validator = 1; + + // Tendermint consensus voting power + int64 voting_power = 2; + + // RewardWeight: Weight of rewards the validator should receive in units of + // consensus power. + int64 reward_weight = 3; + + // Number of valid votes for which the validator will be rewarded + int64 win_count = 4; + + // Number of abstained votes for which there will be no reward or punishment + int64 abstain_count = 5; + + // Number of invalid/punishable votes + int64 miss_count = 6; +} diff --git a/proto/nibiru/oracle/v1/genesis.proto b/proto/nibiru/oracle/v1/genesis.proto index 1ff78bbfb..c5603729c 100644 --- a/proto/nibiru/oracle/v1/genesis.proto +++ b/proto/nibiru/oracle/v1/genesis.proto @@ -5,7 +5,7 @@ import "gogoproto/gogo.proto"; import "nibiru/oracle/v1/oracle.proto"; import "cosmos/base/v1beta1/coin.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/oracle/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/oracle/types"; // GenesisState defines the oracle module's genesis state. message GenesisState { @@ -24,7 +24,7 @@ message GenesisState { aggregate_exchange_rate_votes = 6 [ (gogoproto.nullable) = false ]; repeated string pairs = 7 [ (gogoproto.customtype) = - "github.com/NibiruChain/nibiru/x/common/asset.Pair", + "github.com/NibiruChain/nibiru/v2/x/common/asset.Pair", (gogoproto.nullable) = false ]; repeated nibiru.oracle.v1.Rewards rewards = 8 diff --git a/proto/nibiru/oracle/v1/oracle.proto b/proto/nibiru/oracle/v1/oracle.proto index d9969b32e..76378ff90 100644 --- a/proto/nibiru/oracle/v1/oracle.proto +++ b/proto/nibiru/oracle/v1/oracle.proto @@ -5,12 +5,11 @@ import "gogoproto/gogo.proto"; import "google/protobuf/duration.proto"; import "cosmos/base/v1beta1/coin.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/oracle/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/oracle/types"; // Params defines the module parameters for the x/oracle module. message Params { option (gogoproto.equal) = true; - option (gogoproto.goproto_stringer) = false; // VotePeriod defines the number of blocks during which voting takes place. uint64 vote_period = 1 [ (gogoproto.moretags) = "yaml:\"vote_period\"" ]; @@ -39,7 +38,7 @@ message Params { // Ex. '["unibi:uusd","ubtc:uusd"]' repeated string whitelist = 4 [ (gogoproto.moretags) = "yaml:\"whitelist\"", - (gogoproto.customtype) = "github.com/NibiruChain/nibiru/x/common/asset.Pair" + (gogoproto.customtype) = "github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" ]; // SlashFraction returns the proportion of an oracle's stake that gets // slashed in the event of slashing. `SlashFraction` specifies the exact @@ -60,7 +59,8 @@ message Params { (gogoproto.nullable) = false ]; - // Amount of time to look back for TWAP calculations + // Amount of time to look back for TWAP calculations. + // Ex: "900.000000069s" corresponds to 900 seconds and 69 nanoseconds in JSON. google.protobuf.Duration twap_lookback_window = 8 [ (gogoproto.nullable) = false, (gogoproto.stdduration) = true, @@ -79,9 +79,8 @@ message Params { (gogoproto.nullable) = false ]; - uint64 expiration_blocks = 11 [ - (gogoproto.moretags) = "yaml:\"expiration_blocks\"" - ]; + uint64 expiration_blocks = 11 + [ (gogoproto.moretags) = "yaml:\"expiration_blocks\"" ]; } // Struct for aggregate prevoting on the ExchangeRateVote. @@ -91,7 +90,6 @@ message Params { message AggregateExchangeRatePrevote { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; - option (gogoproto.goproto_stringer) = false; string hash = 1 [ (gogoproto.moretags) = "yaml:\"hash\"" ]; string voter = 2 [ (gogoproto.moretags) = "yaml:\"voter\"" ]; @@ -103,7 +101,6 @@ message AggregateExchangeRatePrevote { message AggregateExchangeRateVote { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; - option (gogoproto.goproto_stringer) = false; repeated ExchangeRateTuple exchange_rate_tuples = 1 [ (gogoproto.moretags) = "yaml:\"exchange_rate_tuples\"", @@ -118,12 +115,11 @@ message AggregateExchangeRateVote { message ExchangeRateTuple { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; - option (gogoproto.goproto_stringer) = false; string pair = 1 [ (gogoproto.moretags) = "yaml:\"pair\"", (gogoproto.customtype) = - "github.com/NibiruChain/nibiru/x/common/asset.Pair", + "github.com/NibiruChain/nibiru/v2/x/common/asset.Pair", (gogoproto.nullable) = false ]; @@ -134,7 +130,7 @@ message ExchangeRateTuple { ]; } -message DatedPrice { +message ExchangeRateAtBlock { string exchange_rate = 1 [ (gogoproto.moretags) = "yaml:\"exchange_rate\"", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", @@ -142,6 +138,11 @@ message DatedPrice { ]; uint64 created_block = 2 [ (gogoproto.moretags) = "yaml:\"created_block\"" ]; + + // Block timestamp for the block where the oracle came to consensus for this + // price. This timestamp is a conventional Unix millisecond time, i.e. the + // number of milliseconds elapsed since January 1, 1970 UTC. + int64 block_timestamp_ms = 3 [ (gogoproto.moretags) = "yaml:\"block_timestamp_ms\"" ]; } // Rewards defines a credit object towards validators @@ -154,4 +155,4 @@ message Rewards { uint64 vote_periods = 2; // Coins defines the amount of coins to distribute in a single vote period. repeated cosmos.base.v1beta1.Coin coins = 3 [ (gogoproto.nullable) = false ]; -} \ No newline at end of file +} diff --git a/proto/nibiru/oracle/v1/query.proto b/proto/nibiru/oracle/v1/query.proto index 993640b40..925042721 100644 --- a/proto/nibiru/oracle/v1/query.proto +++ b/proto/nibiru/oracle/v1/query.proto @@ -6,11 +6,12 @@ import "google/api/annotations.proto"; import "nibiru/oracle/v1/oracle.proto"; import "cosmos/base/v1beta1/coin.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/oracle/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/oracle/types"; // Query defines the gRPC querier service. service Query { - // ExchangeRate returns exchange rate of a pair + // ExchangeRate returns exchange rate of a pair along with the block height and + // block time that the exchange rate was set by the oracle module. rpc ExchangeRate(QueryExchangeRateRequest) returns (QueryExchangeRateResponse) { option (google.api.http).get = "/nibiru/oracle/v1beta1/exchange_rate"; @@ -95,7 +96,7 @@ message QueryExchangeRateRequest { // pair defines the pair to query for. string pair = 1 [ (gogoproto.customtype) = - "github.com/NibiruChain/nibiru/x/common/asset.Pair", + "github.com/NibiruChain/nibiru/v2/x/common/asset.Pair", (gogoproto.nullable) = false ]; } @@ -108,11 +109,20 @@ message QueryExchangeRateResponse { (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false ]; + + // Block timestamp for the block where the oracle came to consensus for this + // price. This timestamp is a conventional Unix millisecond time, i.e. the + // number of milliseconds elapsed since January 1, 1970 UTC. + int64 block_timestamp_ms = 2; + + // Block height when the oracle came to consensus for this price. + uint64 block_height = 3; } // QueryExchangeRatesRequest is the request type for the Query/ExchangeRates RPC // method. -message QueryExchangeRatesRequest {} +message QueryExchangeRatesRequest { +} // QueryExchangeRatesResponse is response type for the // Query/ExchangeRates RPC method. @@ -134,7 +144,7 @@ message QueryActivesResponse { // actives defines a list of the pair which oracle prices agreed upon. repeated string actives = 1 [ (gogoproto.customtype) = - "github.com/NibiruChain/nibiru/x/common/asset.Pair", + "github.com/NibiruChain/nibiru/v2/x/common/asset.Pair", (gogoproto.nullable) = false ]; } @@ -150,7 +160,7 @@ message QueryVoteTargetsResponse { // should vote in the current vote period. repeated string vote_targets = 1 [ (gogoproto.customtype) = - "github.com/NibiruChain/nibiru/x/common/asset.Pair", + "github.com/NibiruChain/nibiru/v2/x/common/asset.Pair", (gogoproto.nullable) = false ]; } diff --git a/proto/nibiru/oracle/v1/state.proto b/proto/nibiru/oracle/v1/state.proto index 96d9bd959..ac7828fd2 100644 --- a/proto/nibiru/oracle/v1/state.proto +++ b/proto/nibiru/oracle/v1/state.proto @@ -6,14 +6,14 @@ import "google/api/annotations.proto"; import "nibiru/oracle/v1/oracle.proto"; import "cosmos/base/v1beta1/coin.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/oracle/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/oracle/types"; // a snapshot of the prices at a given point in time message PriceSnapshot { string pair = 1 [ (gogoproto.moretags) = "yaml:\"pair\"", (gogoproto.customtype) = - "github.com/NibiruChain/nibiru/x/common/asset.Pair", + "github.com/NibiruChain/nibiru/v2/x/common/asset.Pair", (gogoproto.nullable) = false ]; @@ -24,4 +24,4 @@ message PriceSnapshot { // milliseconds since unix epoch int64 timestamp_ms = 3; -} \ No newline at end of file +} diff --git a/proto/nibiru/oracle/v1/tx.proto b/proto/nibiru/oracle/v1/tx.proto index dfc30f45e..8fa0175e0 100644 --- a/proto/nibiru/oracle/v1/tx.proto +++ b/proto/nibiru/oracle/v1/tx.proto @@ -6,7 +6,7 @@ import "google/api/annotations.proto"; import "google/protobuf/duration.proto"; import "nibiru/oracle/v1/oracle.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/oracle/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/oracle/types"; // Msg defines the oracle Msg service. service Msg { @@ -137,7 +137,7 @@ message OracleParamsMsg { // Ex. '["unibi:uusd","ubtc:uusd"]' repeated string whitelist = 4 [ (gogoproto.moretags) = "yaml:\"whitelist\"", - (gogoproto.customtype) = "github.com/NibiruChain/nibiru/x/common/asset.Pair", + (gogoproto.customtype) = "github.com/NibiruChain/nibiru/v2/x/common/asset.Pair", (gogoproto.nullable) = true ]; // SlashFraction returns the proportion of an oracle's stake that gets diff --git a/proto/nibiru/sudo/v1/event.proto b/proto/nibiru/sudo/v1/event.proto index f40344447..15c949d1c 100644 --- a/proto/nibiru/sudo/v1/event.proto +++ b/proto/nibiru/sudo/v1/event.proto @@ -6,8 +6,9 @@ import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "nibiru/sudo/v1/state.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/sudo/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/sudo/types"; +// EventUpdateSudoers: ABCI event emitted upon execution of "MsgEditSudoers". message EventUpdateSudoers { nibiru.sudo.v1.Sudoers sudoers = 1 [ (gogoproto.nullable) = false ]; diff --git a/proto/nibiru/sudo/v1/query.proto b/proto/nibiru/sudo/v1/query.proto index 240f63c4e..18e69ea0e 100644 --- a/proto/nibiru/sudo/v1/query.proto +++ b/proto/nibiru/sudo/v1/query.proto @@ -7,7 +7,7 @@ import "google/api/annotations.proto"; import "nibiru/sudo/v1/state.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/sudo/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/sudo/types"; // Query defines the gRPC querier service. service Query { diff --git a/proto/nibiru/sudo/v1/state.proto b/proto/nibiru/sudo/v1/state.proto index 164d18937..9d01589f4 100644 --- a/proto/nibiru/sudo/v1/state.proto +++ b/proto/nibiru/sudo/v1/state.proto @@ -5,10 +5,9 @@ package nibiru.sudo.v1; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/sudo/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/sudo/types"; message Sudoers { - option (gogoproto.goproto_stringer) = false; // Root: The "root" user. string root = 1; @@ -17,5 +16,5 @@ message Sudoers { repeated string contracts = 2; } -// GenesisState defines the module's genesis state. +// GenesisState: State for migrations and genesis for the x/sudo module. message GenesisState { Sudoers sudoers = 1 [ (gogoproto.nullable) = false ]; } diff --git a/proto/nibiru/sudo/v1/tx.proto b/proto/nibiru/sudo/v1/tx.proto index 95c719962..79be3fa4e 100644 --- a/proto/nibiru/sudo/v1/tx.proto +++ b/proto/nibiru/sudo/v1/tx.proto @@ -5,7 +5,7 @@ package nibiru.sudo.v1; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/sudo/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/sudo/types"; // Msg defines the x/sudo module's Msg service. Protobuf `Msg` services are // called from `BaseApp` instances during `DeliverTx`. The `Msg` service will be @@ -53,4 +53,4 @@ message MsgChangeRoot { } // MsgChangeRootResponse indicates the successful execution of MsgChangeRoot. -message MsgChangeRootResponse {} \ No newline at end of file +message MsgChangeRootResponse {} diff --git a/proto/nibiru/tokenfactory/v1/event.proto b/proto/nibiru/tokenfactory/v1/event.proto index 5364da5db..22e389762 100644 --- a/proto/nibiru/tokenfactory/v1/event.proto +++ b/proto/nibiru/tokenfactory/v1/event.proto @@ -6,7 +6,7 @@ import "cosmos/bank/v1beta1/bank.proto"; import "cosmos/base/v1beta1/coin.proto"; import "gogoproto/gogo.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/tokenfactory/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types"; message EventCreateDenom { string denom = 1; diff --git a/proto/nibiru/tokenfactory/v1/query.proto b/proto/nibiru/tokenfactory/v1/query.proto index 2e3150d09..4fbd5e657 100644 --- a/proto/nibiru/tokenfactory/v1/query.proto +++ b/proto/nibiru/tokenfactory/v1/query.proto @@ -7,7 +7,7 @@ import "google/api/annotations.proto"; import "cosmos/bank/v1beta1/bank.proto"; import "nibiru/tokenfactory/v1/state.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/tokenfactory/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types"; // Query defines the gRPC querier service. service Query { diff --git a/proto/nibiru/tokenfactory/v1/state.proto b/proto/nibiru/tokenfactory/v1/state.proto index 255398aa2..7c457bc63 100644 --- a/proto/nibiru/tokenfactory/v1/state.proto +++ b/proto/nibiru/tokenfactory/v1/state.proto @@ -6,7 +6,7 @@ import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; import "cosmos/base/v1beta1/coin.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/tokenfactory/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types"; // DenomAuthorityMetadata specifies metadata foraddresses that have specific // capabilities over a token factory denom. Right now there is only one Admin @@ -55,7 +55,6 @@ message ModuleParams { // TFDenom is a token factory (TF) denom. The canonical representation is // "tf/{creator}/{subdenom}", its unique denomination in the x/bank module. message TFDenom { - option (gogoproto.goproto_stringer) = false; option (gogoproto.stringer) = false; // Creator: Bech32 address of the creator of the denom. diff --git a/proto/nibiru/tokenfactory/v1/tx.proto b/proto/nibiru/tokenfactory/v1/tx.proto index 8f984d17b..2eeacce46 100644 --- a/proto/nibiru/tokenfactory/v1/tx.proto +++ b/proto/nibiru/tokenfactory/v1/tx.proto @@ -9,7 +9,7 @@ import "cosmos_proto/cosmos.proto"; import "cosmos/bank/v1beta1/bank.proto"; import "nibiru/tokenfactory/v1/state.proto"; -option go_package = "github.com/NibiruChain/nibiru/x/tokenfactory/types"; +option go_package = "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types"; // Msg defines the gRPC Msg service for transactions. service Msg { @@ -125,4 +125,4 @@ message MsgBurnNative { [ (gogoproto.moretags) = "yaml:\"coin\"", (gogoproto.nullable) = false ]; } -message MsgBurnNativeResponse {} \ No newline at end of file +message MsgBurnNativeResponse {} diff --git "a/security-reports/2023-05-Nibiru-Salus-Penetration-Testing-Report-V2-\350\204\261\346\225\217.pdf" "b/security-reports/2023-05-Nibiru-Salus-Penetration-Testing-Report-V2-\350\204\261\346\225\217.pdf" new file mode 100644 index 000000000..d50d1802f Binary files /dev/null and "b/security-reports/2023-05-Nibiru-Salus-Penetration-Testing-Report-V2-\350\204\261\346\225\217.pdf" differ diff --git a/security-reports/2023-07-Nibiru-Zellic-Audit-Report.pdf b/security-reports/2023-07-Nibiru-Zellic-Audit-Report.pdf new file mode 100644 index 000000000..3d9a03167 Binary files /dev/null and b/security-reports/2023-07-Nibiru-Zellic-Audit-Report.pdf differ diff --git a/security-reports/2023-09-30-Nibiru-ITN2-Chain-Halt.md b/security-reports/2023-09-30-Nibiru-ITN2-Chain-Halt.md new file mode 100644 index 000000000..7ed3e3a31 --- /dev/null +++ b/security-reports/2023-09-30-Nibiru-ITN2-Chain-Halt.md @@ -0,0 +1,15 @@ +# Postmortem + +## Summary + +On Sept 30 @ 22:16:01 UTC time, the `nibiru-itn-2` network halted at block `1131575`. + +## Root Cause + +The binary was missing a wasm extension that copies the `wasm` smart contract folder for state syncs. Nodes that joined the network via state sync were missing `wasm` smart contracts. Later on, these nodes became validator nodes, and the set of smart contracts differed between validator nodes. + +A tx in block `1131574` was submitted against one of these missing smart contracts (with code_id 3). Some validators were able to execute the tx successfully and other validators errored out since they didn’t have the wasm smart contract in their local disk. Hence the chain halted while validating the `app_hash` in block `1131575`. + +## Resolution + +The issue was fixed in [PR #1616](https://github.com/NibiruChain/nibiru/pull/1616) and backported to the [v0.21.x release branch](https://github.com/NibiruChain/nibiru/tree/releases/v0.21.x) (currently in [v0.21.11](https://github.com/NibiruChain/nibiru/releases/tag/v0.21.11)). \ No newline at end of file diff --git a/security-reports/README.md b/security-reports/README.md new file mode 100644 index 000000000..fefa16e3c --- /dev/null +++ b/security-reports/README.md @@ -0,0 +1,13 @@ +# Nibiru/security-reports + +Audits and security reports related to Nibiru blockchain, related smart contracts, and core applications. + +## Audit History + +- **2024-11 Nibiru Audit - Code4rena Competitive Audit**: (Report in progress) +- **2024-10 Nibiru Audit - Code4rena Zenith Audit**: (Report in progress) +- **2023-09-30-Nibiru-ITN2-Chain-Halt.md**: Report of a chain halt resulting from +high volatility in Nibiru Oracle exchange rates, causing unwarranted slashing of +the validator set that powers the oracle. +- **2023-07-Nibiru-Zellic-Audit-Report.pdf**: ... +- **2023-05-Nibiru-Salus-Penetration-Testing-Report-V2-脱敏.pdf**: ... diff --git a/simapp/README.md b/simapp/README.md new file mode 100644 index 000000000..13502afe3 --- /dev/null +++ b/simapp/README.md @@ -0,0 +1,59 @@ +# Simulation Tests + +This directory contains the simulation tests for the `simapp` module + +## Test Cases + +### Non-Determinism + +```sh +make test-sim-nondeterminism +``` + +This test case checks that the simulation is deterministic. It does so by +running the simulation twice with the same seed and comparing the resulting +state. If the simulation is deterministic, the resulting state should be the +same. + +### Full App + +```sh +make test-sim-default-genesis-fast +``` + +This test case runs the simulation with the default genesis file. It checks that +the simulation does not panic and that the resulting state is valid. + +### Import/Export + +```sh +make test-sim-import-export +``` + +This test case runs the simulation with the default genesis file. It checks that +the simulation does not panic and that the resulting state is valid. It then +exports the state to a file and imports it back. It checks that the imported +state is the same as the exported state. + +### Simulation After Import + +```sh +make test-sim-after-import +``` + +This test case runs the simulation with the default genesis file. It checks that +the simulation does not panic and that the resulting state is valid. It then +exports the state to a file and imports it back. It checks that the imported +state is the same as the exported state. It then runs the simulation again with +the imported state. It checks that the simulation does not panic and that the +resulting state is valid. + +## Params + +A `params.json` file is included that sets the operation weights for +`CreateValidator` and `EditValidator` to zero. It's a hack to make the +simulation tests pass. The random commission rates sometimes halt the simulation +because the max commission rate is set to 0.25 in the AnteHandler, but sometimes +the random commission rate is higher than that. The random commission is set by +the cosmos-sdk x/staking module simulation operations, so we have no control +over injecting a manual value. diff --git a/simapp/params.json b/simapp/params.json new file mode 100644 index 000000000..67f8d113c --- /dev/null +++ b/simapp/params.json @@ -0,0 +1,4 @@ +{ + "op_weight_msg_create_validator": 0, + "op_weight_msg_edit_validator": 0 +} \ No newline at end of file diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 2b0871a94..ba58da5b8 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -5,107 +5,150 @@ import ( "fmt" "math/rand" "os" + "runtime/debug" + "strings" "testing" - "github.com/cosmos/ibc-go/v7/testing/simapp" - dbm "github.com/cometbft/cometbft-db" - helpers "github.com/cosmos/cosmos-sdk/testutil/sims" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/server" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - simulationtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cosmos/cosmos-sdk/x/simulation" simcli "github.com/cosmos/cosmos-sdk/x/simulation/client/cli" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - appsim "github.com/NibiruChain/nibiru/app/sim" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/codec" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" + epochstypes "github.com/NibiruChain/nibiru/v2/x/epochs/types" + inflationtypes "github.com/NibiruChain/nibiru/v2/x/inflation/types" + oracletypes "github.com/NibiruChain/nibiru/v2/x/oracle/types" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" + tokenfactorytypes "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) // SimAppChainID hardcoded chainID for simulation const SimAppChainID = "simulation-app" func init() { - appsim.GetSimulatorFlags() + // We call GetSimulatorFlags here in order to set the value for + // 'simcli.FlagEnabledValue', which enables simulations + simcli.GetSimulatorFlags() +} + +type StoreKeysPrefixes struct { + A storetypes.StoreKey + B storetypes.StoreKey + Prefixes [][]byte } -func TestFullAppSimulation(tb *testing.T) { +// makeEncodingConfig, similar to [app.MakeEncodingConfig], creates an +// EncodingConfig for an amino based test configuration. However, this function +// registers interfaces and types that are expected by default in the Cosmos-SDK +// even if they are disabled on Nibiru. This is the case for x/vesting Cosmos-SDK +// module. +func makeEncodingConfig() codec.EncodingConfig { + encCfg := app.MakeEncodingConfig() + vesting.RegisterInterfaces(encCfg.InterfaceRegistry) + return encCfg +} + +func TestFullAppSimulation(t *testing.T) { config := simcli.NewConfigFromFlags() config.ChainID = SimAppChainID - db, dir, _, skip, err := helpers.SetupSimulation( - config, - "goleveldb-app-sim", - "Simulation", - simcli.FlagVerboseValue, simcli.FlagEnabledValue, - ) + db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "goleveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue) if skip { - tb.Skip("skipping application simulation") + t.Skip("skipping application simulation") } - require.NoError(tb, err, "simulation setup failed") + require.NoError(t, err, "simulation setup failed") defer func() { - db.Close() - err = os.RemoveAll(dir) - if err != nil { - tb.Fatal(err) - } + require.NoError(t, db.Close()) + require.NoError(t, os.RemoveAll(dir)) }() - encoding := app.MakeEncodingConfig() - app := testapp.NewNibiruTestApp(app.NewDefaultGenesisState(encoding.Marshaler)) + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = app.DefaultNodeHome + appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue - // Run randomized simulation: + encoding := makeEncodingConfig() + app := app.NewNibiruApp(logger, db, nil, true, encoding, appOptions, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "Nibiru", app.Name()) + appCodec := app.AppCodec() + + // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( - /* tb */ tb, - /* w */ os.Stdout, - /* app */ app.BaseApp, - /* appStateFn */ AppStateFn(app.AppCodec(), app.SimulationManager()), - /* randAccFn */ simulationtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - /* ops */ helpers.SimulationOperations(app, app.AppCodec(), config), // Run all registered operations - /* blockedAddrs */ app.ModuleAccountAddrs(), - /* config */ config, - /* cdc */ app.AppCodec(), + t, + os.Stdout, + app.BaseApp, + AppStateFn(appCodec, app.SimulationManager()), + simtypes.RandomAccounts, + simtestutil.SimulationOperations(app, appCodec, config), + app.ModuleAccountAddrs(), + config, + appCodec, ) // export state and simParams before the simulation error is checked - if err = helpers.CheckExportSimulation(app, config, simParams); err != nil { - tb.Fatal(err) - } - - if simErr != nil { - tb.Fatal(simErr) - } + err = simtestutil.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) if config.Commit { - simapp.PrintStats(db) + simtestutil.PrintStats(db) } } +// Tests that the app state hash is deterministic when the operations are run func TestAppStateDeterminism(t *testing.T) { - if !simapp.FlagEnabledValue { + if !simcli.FlagEnabledValue { t.Skip("skipping application simulation") } - encoding := app.MakeEncodingConfig() - - config := simapp.NewConfigFromFlags() + config := simcli.NewConfigFromFlags() config.InitialBlockHeight = 1 config.ExportParamsPath = "" config.OnOperation = false config.AllInvariants = false config.ChainID = SimAppChainID - numSeeds := 3 numTimesToRunPerSeed := 5 + appHashList := make([]json.RawMessage, numTimesToRunPerSeed) + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = app.DefaultNodeHome + appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue for i := 0; i < numSeeds; i++ { config.Seed = rand.Int63() for j := 0; j < numTimesToRunPerSeed; j++ { db := dbm.NewMemDB() - app := testapp.NewNibiruTestApp(app.NewDefaultGenesisState(encoding.Marshaler)) + logger := log.NewNopLogger() + encoding := makeEncodingConfig() + + app := app.NewNibiruApp(logger, db, nil, true, encoding, appOptions, baseapp.SetChainID(SimAppChainID)) + appCodec := app.AppCodec() fmt.Printf( "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", @@ -116,17 +159,17 @@ func TestAppStateDeterminism(t *testing.T) { t, os.Stdout, app.BaseApp, - AppStateFn(app.AppCodec(), app.SimulationManager()), - simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - helpers.SimulationOperations(app, app.AppCodec(), config), + AppStateFn(appCodec, app.SimulationManager()), + simtypes.RandomAccounts, + simtestutil.SimulationOperations(app, appCodec, config), app.ModuleAccountAddrs(), config, - app.AppCodec(), + appCodec, ) require.NoError(t, err) if config.Commit { - simapp.PrintStats(db) + simtestutil.PrintStats(db) } appHash := app.LastCommitID().Hash @@ -141,3 +184,215 @@ func TestAppStateDeterminism(t *testing.T) { } } } + +func TestAppImportExport(t *testing.T) { + config := simcli.NewConfigFromFlags() + config.ChainID = SimAppChainID + + db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "goleveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue) + if skip { + t.Skip("skipping application import/export simulation") + } + require.NoError(t, err, "simulation setup failed") + + defer func() { + require.NoError(t, db.Close()) + require.NoError(t, os.RemoveAll(dir)) + }() + + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = app.DefaultNodeHome + appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue + + encoding := makeEncodingConfig() + oldApp := app.NewNibiruApp(logger, db, nil, true, encoding, appOptions, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "Nibiru", oldApp.Name()) + appCodec := oldApp.AppCodec() + + // Run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + oldApp.BaseApp, + AppStateFn(appCodec, oldApp.SimulationManager()), + simtypes.RandomAccounts, + simtestutil.SimulationOperations(oldApp, oldApp.AppCodec(), config), + oldApp.ModuleAccountAddrs(), + config, + oldApp.AppCodec(), + ) + + // export state and simParams before the simulation error is checked + err = simtestutil.CheckExportSimulation(oldApp, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simtestutil.PrintStats(db) + } + + fmt.Printf("exporting genesis...\n") + + exported, err := oldApp.ExportAppStateAndValidators(false, []string{}, []string{}) + require.NoError(t, err) + + fmt.Printf("importing genesis...\n") + + newDB, newDir, _, _, err := simtestutil.SetupSimulation(config, "goleveldb-app-sim-2", "Simulation-2", simcli.FlagVerboseValue, simcli.FlagEnabledValue) + require.NoError(t, err, "simulation setup failed") + + defer func() { + require.NoError(t, newDB.Close()) + require.NoError(t, os.RemoveAll(newDir)) + }() + + newApp := app.NewNibiruApp(log.NewNopLogger(), newDB, nil, true, encoding, appOptions, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "Nibiru", newApp.Name()) + + var genesisState app.GenesisState + err = json.Unmarshal(exported.AppState, &genesisState) + require.NoError(t, err) + + defer func() { + if r := recover(); r != nil { + err := fmt.Sprintf("%v", r) + if !strings.Contains(err, "validator set is empty after InitGenesis") { + panic(r) + } + logger.Info("Skipping simulation as all validators have been unbonded") + logger.Info("err", err, "stacktrace", string(debug.Stack())) + } + }() + + ctxA := oldApp.NewContext(true, tmproto.Header{Height: oldApp.LastBlockHeight()}) + ctxB := newApp.NewContext(true, tmproto.Header{Height: oldApp.LastBlockHeight()}) + newApp.ModuleManager.InitGenesis(ctxB, oldApp.AppCodec(), genesisState) + newApp.StoreConsensusParams(ctxB, exported.ConsensusParams) + + fmt.Printf("comparing stores...\n") + + storeKeysPrefixes := []StoreKeysPrefixes{ + {oldApp.GetKey(authtypes.StoreKey), newApp.GetKey(authtypes.StoreKey), [][]byte{}}, + { + oldApp.GetKey(stakingtypes.StoreKey), newApp.GetKey(stakingtypes.StoreKey), + [][]byte{ + stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey, + stakingtypes.HistoricalInfoKey, stakingtypes.UnbondingIDKey, stakingtypes.UnbondingIndexKey, stakingtypes.UnbondingTypeKey, stakingtypes.ValidatorUpdatesKey, + }, + }, // ordering may change but it doesn't matter + {oldApp.GetKey(slashingtypes.StoreKey), newApp.GetKey(slashingtypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(minttypes.StoreKey), newApp.GetKey(minttypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(distrtypes.StoreKey), newApp.GetKey(distrtypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(banktypes.StoreKey), newApp.GetKey(banktypes.StoreKey), [][]byte{banktypes.BalancesPrefix}}, + {oldApp.GetKey(paramtypes.StoreKey), newApp.GetKey(paramtypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(govtypes.StoreKey), newApp.GetKey(govtypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(evidencetypes.StoreKey), newApp.GetKey(evidencetypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(capabilitytypes.StoreKey), newApp.GetKey(capabilitytypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(authzkeeper.StoreKey), newApp.GetKey(authzkeeper.StoreKey), [][]byte{authzkeeper.GrantKey, authzkeeper.GrantQueuePrefix}}, + {oldApp.GetKey(devgastypes.StoreKey), newApp.GetKey(devgastypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(epochstypes.StoreKey), newApp.GetKey(epochstypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(inflationtypes.StoreKey), newApp.GetKey(inflationtypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(oracletypes.StoreKey), newApp.GetKey(oracletypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(sudotypes.StoreKey), newApp.GetKey(sudotypes.StoreKey), [][]byte{}}, + {oldApp.GetKey(tokenfactorytypes.StoreKey), newApp.GetKey(tokenfactorytypes.StoreKey), [][]byte{}}, + } + + for _, skp := range storeKeysPrefixes { + storeA := ctxA.KVStore(skp.A) + storeB := ctxB.KVStore(skp.B) + + failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes) + require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") + + fmt.Printf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B) + require.Equal(t, 0, len(failedKVAs), simtestutil.GetSimulationLog(skp.A.Name(), oldApp.SimulationManager().StoreDecoders, failedKVAs, failedKVBs)) + } +} + +func TestAppSimulationAfterImport(t *testing.T) { + config := simcli.NewConfigFromFlags() + config.ChainID = SimAppChainID + + db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "goleveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue) + if skip { + t.Skip("skipping application simulation after import") + } + require.NoError(t, err, "simulation setup failed") + + defer func() { + require.NoError(t, db.Close()) + require.NoError(t, os.RemoveAll(dir)) + }() + + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = app.DefaultNodeHome + appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue + + encoding := makeEncodingConfig() + oldApp := app.NewNibiruApp(logger, db, nil, true, encoding, appOptions, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "Nibiru", oldApp.Name()) + appCodec := oldApp.AppCodec() + + // Run randomized simulation + stopEarly, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + oldApp.BaseApp, + AppStateFn(appCodec, oldApp.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + simtestutil.SimulationOperations(oldApp, appCodec, config), + oldApp.ModuleAccountAddrs(), + config, + appCodec, + ) + + // export state and simParams before the simulation error is checked + err = simtestutil.CheckExportSimulation(oldApp, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simtestutil.PrintStats(db) + } + + if stopEarly { + fmt.Println("can't export or import a zero-validator genesis, exiting test...") + return + } + + fmt.Printf("exporting genesis...\n") + + exported, err := oldApp.ExportAppStateAndValidators(true, []string{}, []string{}) + require.NoError(t, err) + + fmt.Printf("importing genesis...\n") + + newDB, newDir, _, _, err := simtestutil.SetupSimulation(config, "leveldb-app-sim-2", "Simulation-2", simcli.FlagVerboseValue, simcli.FlagEnabledValue) + require.NoError(t, err, "simulation setup failed") + + defer func() { + require.NoError(t, newDB.Close()) + require.NoError(t, os.RemoveAll(newDir)) + }() + + newApp := app.NewNibiruApp(log.NewNopLogger(), newDB, nil, true, encoding, appOptions, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "Nibiru", newApp.Name()) + + newApp.InitChain(abci.RequestInitChain{ + ChainId: SimAppChainID, + AppStateBytes: exported.AppState, + }) + + _, _, err = simulation.SimulateFromSeed( + t, + os.Stdout, + newApp.BaseApp, + AppStateFn(appCodec, newApp.SimulationManager()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + simtestutil.SimulationOperations(newApp, newApp.AppCodec(), config), + newApp.ModuleAccountAddrs(), + config, + oldApp.AppCodec(), + ) + require.NoError(t, err) +} diff --git a/simapp/state_test.go b/simapp/state_test.go index 869e400c0..e48ff7865 100644 --- a/simapp/state_test.go +++ b/simapp/state_test.go @@ -19,8 +19,8 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/NibiruChain/nibiru/app" - appsim "github.com/NibiruChain/nibiru/app/sim" + "github.com/NibiruChain/nibiru/v2/app" + appsim "github.com/NibiruChain/nibiru/v2/app/sim" ) // AppStateFn returns the initial application state using a genesis or the simulation parameters. @@ -64,6 +64,7 @@ func AppStateFn(cdc codec.JSONCodec, simManager *module.SimulationManager) simty if err != nil { panic(err) } + appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) default: @@ -88,7 +89,7 @@ func AppStateFn(cdc codec.JSONCodec, simManager *module.SimulationManager) simty panic(err) } // compute not bonded balance - notBondedTokens := sdk.ZeroInt() + notBondedTokens := math.ZeroInt() for _, val := range stakingState.Validators { if val.Status != stakingtypes.Unbonded { continue diff --git a/token-registry/README.md b/token-registry/README.md new file mode 100644 index 000000000..68f97316c --- /dev/null +++ b/token-registry/README.md @@ -0,0 +1,5 @@ +# Nibiru/token-registry + +This directory implements the Nibiru Token Registry by providing a means to +register offchain digital token metadata to onchain identifiers for use with +applications like wallets. diff --git a/token-registry/assetlist.go b/token-registry/assetlist.go new file mode 100644 index 000000000..d083dc447 --- /dev/null +++ b/token-registry/assetlist.go @@ -0,0 +1,236 @@ +package tokenregistry + +import "bytes" + +func NibiruAssetList() AssetList { + var tokens = TOKENS() + for idx, token := range tokens { + tokens[idx] = token.GitHubify() + } + + return AssetList{ + Schema: "../assetlist.schema.json", + ChainName: "nibiru", + Assets: tokens, + } +} + +func PointImagesToCosmosChainRegistry(assetListJson []byte) (out []byte) { + oldImgPath := []byte("raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img") + newImgPath := []byte("raw.githubusercontent.com/cosmos/chain-registry/master/nibiru/images") + return bytes.ReplaceAll(assetListJson, oldImgPath, newImgPath) +} + +func TOKENS() []Token { + return []Token{ + { + Name: "Nibiru", + Description: "The native token of Nibiru blockchain", + ExtendedDescription: some("Nibiru is a smart contract ecosystem with a high-performance, EVM-equivalent execution layer. Nibiru is engineered to meet the growing demand for versatile, scalable, and easy-to-use Web3 applications."), + Socials: &SocialLinks{ + Website: some("https://nibiru.fi"), + Twitter: some("https://twitter.com/nibiruchain"), + }, + DenomUnits: []DenomUnit{ + {Denom: "unibi", Exponent: 0}, + {Denom: "nibi", Exponent: 6}, + {Denom: "attonibi", Exponent: 18}, + }, + Base: "unibi", + Display: "nibi", + Symbol: "NIBI", + LogoURIs: &LogoURIs{ + Png: some("./img/0000_nibiru.png"), + Svg: some("./img/0000_nibiru.svg"), + }, + CoingeckoID: some("nibiru"), + Images: []AssetImage{ + { + Png: some("./img/0000_nibiru.png"), + Svg: some("./img/0000_nibiru.svg"), + Theme: &ImageTheme{ + PrimaryColorHex: some("#14c0ce"), + }, + }, + }, + TypeAsset: TypeAsset_SDKCoin, + }, + { + Name: "Liquid Staked Nibiru (Eris)", + Description: "Liquid Staked Nibiru (Eris)", + ExtendedDescription: some("Liquid Staked Nibiru, powered by Eris Protocol's amplifier contracts. Nibiru is a smart contract ecosystem with a high-performance, EVM-equivalent execution layer. Nibiru is engineered to meet the growing demand for versatile, scalable, and easy-to-use Web3 applications."), + Socials: &SocialLinks{ + Website: some("https://nibiru.fi/docs/learn/liquid-stake/"), + Twitter: some("https://x.com/eris_protocol"), + }, + DenomUnits: []DenomUnit{ + {Denom: "tf/nibi1udqqx30cw8nwjxtl4l28ym9hhrp933zlq8dqxfjzcdhvl8y24zcqpzmh8m/ampNIBI", Exponent: 0}, + {Denom: "stNIBI", Exponent: 6}, + }, + Base: "tf/nibi1udqqx30cw8nwjxtl4l28ym9hhrp933zlq8dqxfjzcdhvl8y24zcqpzmh8m/ampNIBI", + Display: "stNIBI", + Symbol: "stNIBI", + LogoURIs: &LogoURIs{ + Png: some("./img/0001_stnibi-500x500.png"), + Svg: some("./img/0001_stnibi-500x500.svg"), + }, + Images: []AssetImage{ + { + Png: some("./img/0001_stnibi-500x500.png"), + Svg: some("./img/0001_stnibi-500x500.svg"), + Theme: &ImageTheme{ + PrimaryColorHex: some("#14c0ce"), + }, + }, + }, + Traces: []Trace{ + { + Type: "liquid-stake", + Counterparty: Counterparty{ + ChainName: "nibiru", + BaseDenom: "unibi", + }, + Provider: some("Eris Protocol"), + }, + }, + TypeAsset: TypeAsset_SDKCoin, + }, + { + Name: "Noble USDC", + Description: "Noble USDC on Nibiru", + DenomUnits: []DenomUnit{ + {Denom: "ibc/F082B65C88E4B6D5EF1DB243CDA1D331D002759E938A0F5CD3FFDC5D53B3E349", Exponent: 0}, + {Denom: "usdc", Exponent: 6}, + }, + Base: "ibc/F082B65C88E4B6D5EF1DB243CDA1D331D002759E938A0F5CD3FFDC5D53B3E349", + Display: "usdc", + Symbol: "USDC", + Traces: []Trace{ + { + Type: TraceType_IBC, + Counterparty: Counterparty{ + ChainName: "noble", + BaseDenom: "uusdc", + ChannelID: some("channel-67"), + }, + Chain: &TraceChainInfo{ + ChannelID: "channel-2", + Path: "transfer/channel-2/uusdc", + }, + }, + }, + Images: []AssetImage{ + { + ImageSync: &ImageSync{ + ChainName: "noble", + BaseDenom: "uusdc", + }, + Png: some("./img/0002_usdc.png"), + Svg: some("./img/0002_usdc.svg"), + Theme: &ImageTheme{ + Circle: some(true), + PrimaryColorHex: some("#2775CA"), + }, + }, + }, + LogoURIs: &LogoURIs{ + Png: some("./img/0002_usdc.png"), + Svg: some("./img/0002_usdc.svg"), + }, + TypeAsset: TypeAsset_ICS20, + }, + + { + Name: "Astrovault token", + Description: "AXV", + ExtendedDescription: some("AXV is the Astrovault token."), + Socials: &SocialLinks{ + Website: some("https://astrovault.io/"), + Twitter: some("https://x.com/axvdex"), + }, + DenomUnits: []DenomUnit{ + {Denom: "tf/nibi1vetfuua65frvf6f458xgtjerf0ra7wwjykrdpuyn0jur5x07awxsfka0ga/axv", Exponent: 0}, + {Denom: "AXV", Exponent: 6}, + }, + Base: "tf/nibi1vetfuua65frvf6f458xgtjerf0ra7wwjykrdpuyn0jur5x07awxsfka0ga/axv", + Display: "AXV", + Symbol: "AXV", + LogoURIs: &LogoURIs{ + Png: some("./img/0003_astrovault-axv.png"), + Svg: some("./img/0003_astrovault-axv.svg"), + }, + Images: []AssetImage{ + { + ImageSync: &ImageSync{ + ChainName: "neutron", + BaseDenom: "cw20:neutron10dxyft3nv4vpxh5vrpn0xw8geej8dw3g39g7nqp8mrm307ypssksau29af", + }, + Png: some("./img/0003_astrovault-axv.png"), + Svg: some("./img/0003_astrovault-axv.svg"), + }, + }, + TypeAsset: TypeAsset_SDKCoin, + }, + + { + Name: "Astrovault Nibiru LST (xNIBI)", + Description: "Astrovault Nibiru LST (xNIBI)", + TypeAsset: TypeAsset_CW20, + Address: some("nibi1cehpv50vl90g9qkwwny8mw7txw79zs6f7wsfe8ey7dgp238gpy4qhdqjhm"), + ExtendedDescription: some("xNIBI is a liquid staking derivative for NIBI created by Astrovault."), + Socials: &SocialLinks{ + Website: some("https://astrovault.io/"), + Twitter: some("https://x.com/axvdex"), + }, + DenomUnits: []DenomUnit{ + {Denom: "cw20:nibi1cehpv50vl90g9qkwwny8mw7txw79zs6f7wsfe8ey7dgp238gpy4qhdqjhm", Exponent: 0}, + {Denom: "xNIBI", Exponent: 6}, + }, + Base: "cw20:nibi1cehpv50vl90g9qkwwny8mw7txw79zs6f7wsfe8ey7dgp238gpy4qhdqjhm", + Display: "xNIBI", + Symbol: "xNIBI", + LogoURIs: &LogoURIs{ + Svg: some("./img/0004_astrovault-xnibi.svg"), + }, + Images: []AssetImage{ + { + Svg: some("./img/0004_astrovault-xnibi.svg"), + }, + }, + }, + + { + Description: "uoprek", + DenomUnits: []DenomUnit{ + {Denom: "tf/nibi149m52kn7nvsg5nftvv4fh85scsavpdfxp5nr7zasz97dum89dp5qkyhy0t/uoprek", Exponent: 0}, + }, + Base: "tf/nibi149m52kn7nvsg5nftvv4fh85scsavpdfxp5nr7zasz97dum89dp5qkyhy0t/uoprek", + Name: "uoprek", + Display: "tf/nibi149m52kn7nvsg5nftvv4fh85scsavpdfxp5nr7zasz97dum89dp5qkyhy0t/uoprek", + Symbol: "UOPREK", + TypeAsset: TypeAsset_SDKCoin, + }, + { + Description: "utestate", + DenomUnits: []DenomUnit{ + {Denom: "tf/nibi1lp28kx3gz0prsztl024z730ufkg3alahaq3e7a6gae22nk0dqdvsyrrgqw/utestate", Exponent: 0}, + }, + Base: "tf/nibi1lp28kx3gz0prsztl024z730ufkg3alahaq3e7a6gae22nk0dqdvsyrrgqw/utestate", + Name: "utestate", + Display: "tf/nibi1lp28kx3gz0prsztl024z730ufkg3alahaq3e7a6gae22nk0dqdvsyrrgqw/utestate", + Symbol: "UTESTATE", + TypeAsset: TypeAsset_SDKCoin, + }, + { + Description: "npp", + DenomUnits: []DenomUnit{ + {Denom: "tf/nibi1xpp7yn0tce62ffattws3gpd6v0tah0mlevef3ej3r4pnfvsehcgqk3jvxq/NPP", Exponent: 0}, + }, + Base: "tf/nibi1xpp7yn0tce62ffattws3gpd6v0tah0mlevef3ej3r4pnfvsehcgqk3jvxq/NPP", + Name: "npp", + Display: "tf/nibi1xpp7yn0tce62ffattws3gpd6v0tah0mlevef3ej3r4pnfvsehcgqk3jvxq/NPP", + Symbol: "NPP", + TypeAsset: TypeAsset_SDKCoin, + }, + } +} diff --git a/token-registry/assetlist_test.go b/token-registry/assetlist_test.go new file mode 100644 index 000000000..06fcfb1c1 --- /dev/null +++ b/token-registry/assetlist_test.go @@ -0,0 +1,182 @@ +package tokenregistry_test + +import ( + "os" + "strings" + "testing" + + "github.com/stretchr/testify/suite" + + tokenregistry "github.com/NibiruChain/nibiru/v2/token-registry" +) + +type Suite struct { + suite.Suite +} + +func TestSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} + +func (s *Suite) TestImagesPresent() { + assetList := tokenregistry.NibiruAssetList() + for _, token := range assetList.Assets { + s.Run(token.Name, func() { + token = token.GitHubify() + + // Make sure all local images in Token.LogoURIs exist + if token.LogoURIs != nil { + png, svg := token.LogoURIs.Png, token.LogoURIs.Svg + if png != nil && strings.Contains(*png, "token-registry/img/") { + localImgPath := "./img/" + strings.Split(*png, "/img/")[1] + _, err := os.Stat(localImgPath) + s.NoError(err) + } + if svg != nil && strings.Contains(*svg, "token-registry/img/") { + localImgPath := "./img/" + strings.Split(*svg, "/img/")[1] + _, err := os.Stat(localImgPath) + s.NoError(err) + } + } + + // Make sure all local images in Token.Images exist + for _, img := range token.Images { + png, svg := img.Png, img.Svg + if png != nil && strings.Contains(*png, "token-registry/img/") { + localImgPath := "./img/" + strings.Split(*png, "/img/")[1] + _, err := os.Stat(localImgPath) + s.NoError(err) + } + if svg != nil && strings.Contains(*svg, "token-registry/img/") { + localImgPath := "./img/" + strings.Split(*svg, "/img/")[1] + _, err := os.Stat(localImgPath) + s.NoError(err) + } + } + }) + } +} + +func some(s string) *string { + return &s +} + +func (s *Suite) TestLogoURIsGitHubify() { + tests := []struct { + name string + input tokenregistry.LogoURIs + expected tokenregistry.LogoURIs + }{ + { + name: "Png and Svg are local paths", + input: tokenregistry.LogoURIs{ + Png: some("./img/logo.png"), + Svg: some("./img/logo.svg"), + }, + expected: tokenregistry.LogoURIs{ + Png: some("https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/logo.png"), + Svg: some("https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/logo.svg"), + }, + }, + { + name: "Png is local, Svg is external", + input: tokenregistry.LogoURIs{ + Png: some("./img/logo.png"), + Svg: some("https://example.com/logo.svg"), + }, + expected: tokenregistry.LogoURIs{ + Png: some("https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/logo.png"), + Svg: some("https://example.com/logo.svg"), + }, + }, + { + name: "Both Png and Svg are external URLs", + input: tokenregistry.LogoURIs{ + Png: some("https://example.com/logo.png"), + Svg: some("https://example.com/logo.svg"), + }, + expected: tokenregistry.LogoURIs{ + Png: some("https://example.com/logo.png"), + Svg: some("https://example.com/logo.svg"), + }, + }, + { + name: "Both Png and Svg are nil", + input: tokenregistry.LogoURIs{ + Png: nil, + Svg: nil, + }, + expected: tokenregistry.LogoURIs{ + Png: nil, + Svg: nil, + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + result := tt.input.GitHubify() + s.Equal(tt.expected, *result) + }) + } +} + +func (s *Suite) TestAssetImageGitHubify() { + tests := []struct { + name string + input tokenregistry.AssetImage + expected tokenregistry.AssetImage + }{ + { + name: "Png and Svg are local paths", + input: tokenregistry.AssetImage{ + Png: some("./img/asset.png"), + Svg: some("./img/asset.svg"), + }, + expected: tokenregistry.AssetImage{ + Png: some("https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/asset.png"), + Svg: some("https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/asset.svg"), + }, + }, + { + name: "Png is local, Svg is external", + input: tokenregistry.AssetImage{ + Png: some("./img/asset.png"), + Svg: some("https://example.com/asset.svg"), + }, + expected: tokenregistry.AssetImage{ + Png: some("https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/asset.png"), + Svg: some("https://example.com/asset.svg"), + }, + }, + { + name: "Both Png and Svg are external URLs", + input: tokenregistry.AssetImage{ + Png: some("https://example.com/asset.png"), + Svg: some("https://example.com/asset.svg"), + }, + expected: tokenregistry.AssetImage{ + Png: some("https://example.com/asset.png"), + Svg: some("https://example.com/asset.svg"), + }, + }, + { + name: "Both Png and Svg are nil", + input: tokenregistry.AssetImage{ + Png: nil, + Svg: nil, + }, + expected: tokenregistry.AssetImage{ + Png: nil, + Svg: nil, + }, + }, + } + + for _, tt := range tests { + s.Run(tt.name, func() { + result := tt.input.GitHubify() + s.Equal(tt.expected, result) + }) + } +} diff --git a/token-registry/githubify.go b/token-registry/githubify.go new file mode 100644 index 000000000..7bedf6a3d --- /dev/null +++ b/token-registry/githubify.go @@ -0,0 +1,63 @@ +package tokenregistry + +import "strings" + +func (t Token) GitHubify() Token { + out := t + + if out.LogoURIs != nil { + out.LogoURIs = out.LogoURIs.GitHubify() + } + + for imgIdx, img := range out.Images { + out.Images[imgIdx] = img.GitHubify() + } + + return out +} + +// localImageToGitHub converts a path to a local image into a GitHub download +// link in the NibiruChain/nibiru repository. +func localImageToGitHub(local string) string { + trimmed := strings.TrimPrefix(local, "./img/") + return "https://raw.githubusercontent.com/NibiruChain/nibiru/main/token-registry/img/" + trimmed +} + +// isLocalImage returns true if an image URI is meant for a local file in +// the "token-registry/img" directory. +func isLocalImage(maybeLocal *string) bool { + if maybeLocal == nil { + return false + } + return strings.HasPrefix(*maybeLocal, "./img") +} + +func (logouris LogoURIs) GitHubify() *LogoURIs { + out := new(LogoURIs) + out.Png, out.Svg = logouris.Png, logouris.Svg + if logouris.Png != nil && isLocalImage(logouris.Png) { + out.Png = some(localImageToGitHub(*logouris.Png)) + } + if logouris.Svg != nil && isLocalImage(logouris.Svg) { + out.Svg = some(localImageToGitHub(*logouris.Svg)) + } + return out +} + +func (ai AssetImage) GitHubify() AssetImage { + out := AssetImage{} + out.Png, out.Svg = ai.Png, ai.Svg + if ai.Png != nil && isLocalImage(ai.Png) { + out.Png = some(localImageToGitHub(*ai.Png)) + } + if ai.Svg != nil && isLocalImage(ai.Svg) { + out.Svg = some(localImageToGitHub(*ai.Svg)) + } + if ai.Theme != nil { + out.Theme = ai.Theme + } + if ai.ImageSync != nil { + out.ImageSync = ai.ImageSync + } + return out +} diff --git a/token-registry/img/0000_nibiru.png b/token-registry/img/0000_nibiru.png new file mode 100644 index 000000000..46dbd6488 Binary files /dev/null and b/token-registry/img/0000_nibiru.png differ diff --git a/token-registry/img/0000_nibiru.svg b/token-registry/img/0000_nibiru.svg new file mode 100644 index 000000000..15eabe343 --- /dev/null +++ b/token-registry/img/0000_nibiru.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/token-registry/img/0001_stnibi-500x500.png b/token-registry/img/0001_stnibi-500x500.png new file mode 100644 index 000000000..0b0131a3e Binary files /dev/null and b/token-registry/img/0001_stnibi-500x500.png differ diff --git a/token-registry/img/0001_stnibi-500x500.svg b/token-registry/img/0001_stnibi-500x500.svg new file mode 100644 index 000000000..f8e2b3b7a --- /dev/null +++ b/token-registry/img/0001_stnibi-500x500.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/token-registry/img/0002_usdc.png b/token-registry/img/0002_usdc.png new file mode 100644 index 000000000..8c0ffa7b8 Binary files /dev/null and b/token-registry/img/0002_usdc.png differ diff --git a/token-registry/img/0002_usdc.svg b/token-registry/img/0002_usdc.svg new file mode 100644 index 000000000..bcec78210 --- /dev/null +++ b/token-registry/img/0002_usdc.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/token-registry/img/0003_astrovault-axv.png b/token-registry/img/0003_astrovault-axv.png new file mode 100644 index 000000000..561bb4d5e Binary files /dev/null and b/token-registry/img/0003_astrovault-axv.png differ diff --git a/token-registry/img/0003_astrovault-axv.svg b/token-registry/img/0003_astrovault-axv.svg new file mode 100644 index 000000000..7039be027 --- /dev/null +++ b/token-registry/img/0003_astrovault-axv.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/token-registry/img/0004_astrovault-xnibi.svg b/token-registry/img/0004_astrovault-xnibi.svg new file mode 100644 index 000000000..158c3ca12 --- /dev/null +++ b/token-registry/img/0004_astrovault-xnibi.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/token-registry/main/main.go b/token-registry/main/main.go new file mode 100644 index 000000000..ce32e15bc --- /dev/null +++ b/token-registry/main/main.go @@ -0,0 +1,85 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path" + "strings" + + "github.com/rs/zerolog/log" + + tokenregistry "github.com/NibiruChain/nibiru/v2/token-registry" +) + +// findRootPath returns the absolute path of the repository root +// This is retrievable with: go list -m -f {{.Dir}} +func findRootPath() (string, error) { + // rootPath, _ := exec.Command("go list -m -f {{.Dir}}").Output() + // This returns the path to the root of the project. + rootPathBz, err := exec.Command("go", "list", "-m", "-f", "{{.Dir}}").Output() + if err != nil { + return "", err + } + rootPath := strings.Trim(string(rootPathBz), "\n") + return rootPath, nil +} + +const SAVE_PATH_ASSETLIST = "dist/assetlist.json" +const SAVE_PATH_COSMOS_ASSETLIST = "dist/cosmos-assetlist.json" + +func main() { + assetList := tokenregistry.NibiruAssetList() + + prettyBz, err := json.MarshalIndent(assetList, "", " ") + if err != nil { + log.Error().Msg(err.Error()) + return + } + + rootPath, err := findRootPath() + if err != nil { + log.Error().Msg(err.Error()) + return + } + savePath := path.Join(rootPath, SAVE_PATH_ASSETLIST) + + // Create the dist directory if it does not exist + distDirPath := path.Join(rootPath, "dist") + if _, err := os.Stat(distDirPath); os.IsNotExist(err) { + if err := os.Mkdir(distDirPath, 0755); err != nil { + log.Error().Msg(err.Error()) + return + } + } + + perm := os.FileMode(0666) // All can read and write + err = os.WriteFile(savePath, prettyBz, perm) + if err != nil { + log.Error().Msg(err.Error()) + return + } + + savePath = path.Join(rootPath, SAVE_PATH_COSMOS_ASSETLIST) + saveBz := tokenregistry.PointImagesToCosmosChainRegistry(prettyBz) + err = os.WriteFile(savePath, saveBz, perm) + if err != nil { + log.Error().Msg(err.Error()) + return + } + + fmt.Printf("✅ Generation complete!\n") + fmt.Printf( + "File \"%v\" contains the asset list using images only from the Nibiru repo\n", + SAVE_PATH_ASSETLIST, + ) + fmt.Printf( + "File \"%v\" contains the asset list for the cosmos/chain-registry\n", + SAVE_PATH_COSMOS_ASSETLIST, + ) + fmt.Println("You can submit a PR to cosmos/chain-registry using " + + SAVE_PATH_COSMOS_ASSETLIST + + " as the file chain-registry/nibiru/assetlist.json", + ) +} diff --git a/token-registry/token.go b/token-registry/token.go new file mode 100644 index 000000000..dd22c32db --- /dev/null +++ b/token-registry/token.go @@ -0,0 +1,146 @@ +package tokenregistry + +import ( + "encoding/json" +) + +// some: Helper to create pointers for literals +func some[T any](v T) *T { + return &v +} + +type Token struct { + // The human-readable name of the asset + Name string `json:"name"` + // A short description of the asset + Description string `json:"description"` + // Contract address for the token. Required for [TypeAsset] "erc20" and "cw20". + Address *string `json:"address,omitempty"` + // An extended, detailed description of the asset (optional) + ExtendedDescription *string `json:"extended_description,omitempty"` + // Links to social platforms and official websites (optional) + Socials *SocialLinks `json:"socials,omitempty"` + // Units of denomination for the asset + DenomUnits []DenomUnit `json:"denom_units"` + // The base denomination for the asset (canonical name) + Base string `json:"base"` + // The display name or symbol used in UI for the asset + Display string `json:"display"` + // The ticker or symbol of the asset + Symbol string `json:"symbol"` + // URIs for the asset's logo in different formats (optional) + LogoURIs *LogoURIs `json:"logo_URIs,omitempty"` + // Unique identifier for the asset on Coingecko (optional) + CoingeckoID *string `json:"coingecko_id,omitempty"` + // An array of image representations for the asset (optional) + Images []AssetImage `json:"images,omitempty"` + // Type of the asset (e.g., "sdk.coin", "ics20", "erc20") + TypeAsset TypeAsset `json:"type_asset"` + // Trace data for the asset (optional, for cross-chain or liquid staking assets) + Traces []Trace `json:"traces,omitempty"` +} + +type AssetList struct { + Schema string `json:"$schema"` + ChainName string `json:"chain_name"` + Assets []Token `json:"assets"` +} + +// String returns a "pretty" JSON version of the [AssetList]. +func (a AssetList) String() string { + jsonBz, _ := json.MarshalIndent(a, "", " ") + return string(jsonBz) +} + +type SocialLinks struct { + Website *string `json:"website,omitempty"` + Twitter *string `json:"twitter,omitempty"` +} + +type DenomUnit struct { + Denom string `json:"denom"` + Exponent int `json:"exponent"` + Aliases []string `json:"aliases,omitempty"` +} + +type LogoURIs struct { + Png *string `json:"png,omitempty"` + Svg *string `json:"svg,omitempty"` +} + +type AssetImage struct { + Png *string `json:"png,omitempty"` + Svg *string `json:"svg,omitempty"` + Theme *ImageTheme `json:"theme,omitempty"` + ImageSync *ImageSync `json:"image_sync,omitempty"` +} + +// ImageTheme represents theme customization for an image. +type ImageTheme struct { + // Whether the image should appear in a circular format (optional) + Circle *bool `json:"circle,omitempty"` + // Primary color in hexadecimal format (optional) + PrimaryColorHex *string `json:"primary_color_hex,omitempty"` +} + +// ImageSync represents synchronization details for cross-chain assets. +type ImageSync struct { + // Name of the chain associated with the image + ChainName string `json:"chain_name"` + // Base denomination of the synced asset + BaseDenom string `json:"base_denom"` +} + +// TypeAsset is an enum type for "type_asset". Valid values include "sdk.coin", +// "ics20", and "erc20". See [Examples]. +// +// [Examples]: https://www.notion.so/nibiru/Nibiru-Token-Registry-Info-Fungible-Tokens-on-Nibiru-cf46d37ccd9c4c33bb083e20e0fa8e20?pvs=4 +type TypeAsset string + +const ( + TypeAsset_SDKCoin TypeAsset = "sdk.coin" + TypeAsset_ICS20 TypeAsset = "ics20" + TypeAsset_ERC20 TypeAsset = "erc20" + TypeAsset_CW20 TypeAsset = "cw20" +) + +// Trace represents trace data for cross-chain or liquid staking assets. +type Trace struct { + // Type of trace (e.g., "ibc", "liquid-stake", "wrapped", "bridge") + Type TraceType `json:"type"` + // Counterparty information for the trace + Counterparty Counterparty `json:"counterparty"` + // Provider of the asset for liquid staking or cross-chain trace (optional) + Provider *string `json:"provider,omitempty"` + // Additional chain-level details (optional) + Chain *TraceChainInfo `json:"chain,omitempty"` +} + +// TraceType is an enum type for "trace.type" (e.g. "ibc", "liquid-stake", +// "wrapped", "bridge") +type TraceType string + +const ( + TraceType_IBC TraceType = "ibc" + TraceType_LiquidStake TraceType = "liquid-stake" + TraceType_Wrapped TraceType = "wrapped" + TraceType_Bridge TraceType = "bridge" +) + +// Counterparty represents the counterparty information for an asset trace. +type Counterparty struct { + // Name of the counterparty chain + ChainName string `json:"chain_name"` + // Base denomination on the counterparty chain + BaseDenom string `json:"base_denom"` + // Channel ID used for communication (optional) + ChannelID *string `json:"channel_id,omitempty"` +} + +// TraceChainInfo represents additional chain-level details for an asset trace. +type TraceChainInfo struct { + // Channel ID on the chain + ChannelID string `json:"channel_id"` + // Path used for asset transfer on the chain + Path string `json:"path"` +} diff --git a/tools/ignite-config.yml b/tools/ignite-config.yml new file mode 100644 index 000000000..b073b2632 --- /dev/null +++ b/tools/ignite-config.yml @@ -0,0 +1,32 @@ +# This is a config file for Ignite CLI. +# +# It works with: +# - Cosmos-SDK v0.47.5 +# - Ignite CLI v0.27.2 +# +# To generate and OpenAPI specification (openapi.yml) for use Swagger UI, +# run the following command from the current directory (nibiru/tools). +# ```bash +# ignite generate openapi --yes --path ../ +# ``` +# +# See: +# https://github.com/ignite/cli +# https://github.com/swagger-api/swagger-ui +version: 1 +accounts: + - name: alice + coins: + - 9000555unibi + - name: bob +validators: + - name: guard + bonded: 9000555unibi +faucet: + name: faucet-acc + coins: + - 999999999unibi + - 999999999unusd +client: + openapi: + path: dist/openapi.yml diff --git a/tools/ignite-openapi.sh b/tools/ignite-openapi.sh new file mode 100644 index 000000000..f7dec0fb6 --- /dev/null +++ b/tools/ignite-openapi.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +set -e + +# log_debug: Simple wrapper for `echo` with a DEBUG prefix. +log_debug() { + echo "DEBUG" "$@" +} +log_error() { + echo "❌ Error:" "$@" +} +log_success() { + echo "✅ Success:" "$@" +} + +# ensure_path: Ensures that the script execution starts from the root of the +# repo. +ensure_path() { + + # Get the current directory + local current_dir + current_dir=$(pwd) + + # Get the directory of the script + script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + + local repo_root + repo_root="${script_dir%/tools}" + + + # Check if we're in the root directory or tools directory of the repo + if [ "$current_dir" == "$repo_root" ]; then + # We're in the root, nothing to do + : + elif [ "$current_dir" == "$script_dir" ]; then + # We're in the tools directory, move one level up to the root + cd "$repo_root" + else + # We're neither in the root nor in the tools directory + log_error "You must run this script from the repo root or the tools directory." + log_debug "current_dir: $current_dir" + log_debug "script_dir: $script_dir" + log_debug "repo_root: $repo_root" + exit 1 + fi +} + + +# which_ok: Check if the given binary is in the $PATH. +# Returns code 0 on success and code 1 if the command fails. +which_ok() { + if which "$1" >/dev/null 2>&1; then + return 0 + else + log_error "$1 is not present in \$PATH" + return 1 + fi +} + +# ensure_deps: Make sure expected dependencies are installed. +ensure_deps() { + if ! which_ok ignite; then + log_debug "Installing Ignite CLI (ignite)..." + curl https://get.ignite.com/cli! | bash + if ! which_ok ignite; then + log_error "Failed to install ignite." + return 1 + fi + fi + + which_ok nibid +} + +# Ignite expects files at the base of the repo. Rather than keeping a config +# there just for the Ignite CLI, it's cleaner to symlink the file just for the +# script to run. +symlink_config_to_repo_base() { + local source_file + source_file="tools/ignite-config.yml" + if [[ ! -f "$source_file" ]]; then + log_error "Source file '$source_file' does not exist." + return 1 + fi + + ln -sf $source_file ./config.yml + log_success "symlinked config" +} + +check_ignite_config_health() { + ignite doctor +} + +# ------------------------------ main ----------------------------- +ensure_path +ensure_deps +symlink_config_to_repo_base +check_ignite_config_health +ignite generate openapi --yes +log_success "Completed generation of OpenAPI spec" diff --git a/tools/tools.go b/tools/tools.go new file mode 100644 index 000000000..6e7a12d40 --- /dev/null +++ b/tools/tools.go @@ -0,0 +1,11 @@ +//go:build tools + +package tools + +import ( + _ "github.com/cosmos/gogoproto/protoc-gen-gocosmos" + _ "github.com/golang/protobuf/protoc-gen-go" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" +) diff --git a/wasmbinding/wasm.go b/wasmbinding/wasm.go deleted file mode 100644 index 44a63372c..000000000 --- a/wasmbinding/wasm.go +++ /dev/null @@ -1,27 +0,0 @@ -package wasmbinding - -import ( - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" - - "github.com/NibiruChain/nibiru/x/sudo/keeper" -) - -// NibiruWasmOptions: Wasm Options are extension points to instantiate the Wasm -// keeper with non-default values -func NibiruWasmOptions( - grpcQueryRouter *baseapp.GRPCQueryRouter, - appCodec codec.Codec, - sudoKeeper keeper.Keeper, -) []wasmkeeper.Option { - wasmQueryOption := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{ - Stargate: wasmkeeper.AcceptListStargateQuerier( - WasmAcceptedStargateQueries(), - grpcQueryRouter, - appCodec, - ), - }) - - return []wasmkeeper.Option{wasmQueryOption} -} diff --git a/x/README.md b/x/README.md index 63431e1bb..75ea21f45 100644 --- a/x/README.md +++ b/x/README.md @@ -1,21 +1,3 @@ # Nibiru Modules -| Module | Active? | Description | -| ------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [common][code-x-common] | ✔️ | Holds helper and utility functions to be utilized by other `x/` modules. | -| [epochs][code-x-epochs] | ✔️ | Often in the SDK, we would like to run certain code every-so often. The purpose of `epochs` module is to allow other modules to set that they would like to be signaled once every period. So another module can specify it wants to execute code once a week, starting at UTC-time = x. `epochs` creates a generalized epoch interface to other modules so that they can easily be signalled upon such events. | -| [oracle][code-x-oracle] | ✔️ | Handles the posting of an up-to-date and accurate feed of exchange rates from the validators. | -| [perp][code-x-perp] | ✔️ | Powers the Nibi-Perps exchange. This module enables traders to open long and short leveraged positions and houses all of the PnL calculation and liquidation logic. | -| [spot][code-x-spot] | ✔️ | Responsible for creating, joining, and exiting liquidity pools. It also allows users to swap between two assets in an existing pool. It's a fully functional AMM. | -| [stablecoin][code-x-stablecoin] | ⭕️ | Resonsible for handling mint and redeem transactions with NUSD. | -| [testutil][code-x-testutil] | ✔️ | Helper functions for unit and integration tests. | -| [wasm][code-x-wasm] | ✔️ | Implements the execution environment for [WebAssembly (WASM) smart contracts](https://book.cosmwasm.com/). | - -[code-x-common]: https://github.com/NibiruChain/nibiru/tree/master/x/common -[code-x-epochs]: https://github.com/NibiruChain/nibiru/tree/master/x/epochs -[code-x-oracle]: https://github.com/NibiruChain/nibiru/tree/master/x/oracle -[code-x-perp]: https://github.com/NibiruChain/nibiru/tree/master/x/perp -[code-x-spot]: https://github.com/NibiruChain/nibiru/tree/master/x/spot -[code-x-stablecoin]: https://github.com/NibiruChain/nibiru/tree/master/x/stablecoin -[code-x-testutil]: https://github.com/NibiruChain/nibiru/tree/master/x/testutil -[code-x-wasm]: https://github.com/NibiruChain/nibiru/tree/master/x/wasm \ No newline at end of file +See [BlockChain Modules - Nibiru Docs](https://nibiru.fi/docs/dev/x/) diff --git a/x/common/address.go b/x/common/address.go index 968729361..61ca416d3 100644 --- a/x/common/address.go +++ b/x/common/address.go @@ -1,6 +1,7 @@ package common import ( + "github.com/NibiruChain/collections" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -18,5 +19,16 @@ func StringsToAddrs(strs ...string) []sdk.AccAddress { addr := sdk.MustAccAddressFromBech32(str) addrs = append(addrs, addr) } + return addrs } + +// TODO: (realu) Move to collections library +var StringValueEncoder collections.ValueEncoder[string] = stringValueEncoder{} + +type stringValueEncoder struct{} + +func (a stringValueEncoder) Encode(value string) []byte { return []byte(value) } +func (a stringValueEncoder) Decode(b []byte) string { return string(b) } +func (a stringValueEncoder) Stringify(value string) string { return value } +func (a stringValueEncoder) Name() string { return "string" } diff --git a/x/common/address_test.go b/x/common/address_test.go index 77f156f08..3d02903ec 100644 --- a/x/common/address_test.go +++ b/x/common/address_test.go @@ -3,10 +3,11 @@ package common_test import ( "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common" - "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" ) func TestAddress(t *testing.T) { @@ -17,3 +18,25 @@ func TestAddress(t *testing.T) { require.EqualValues(t, addrs, addrsOut) }) } + +func TestStringValueEncoder(t *testing.T) { + encoder := common.StringValueEncoder + tests := []struct { + given string + }{ + {"hello"}, + {"12345"}, + {""}, + {testutil.AccAddress().String()}, + } + + for _, tc := range tests { + t.Run(tc.given, func(t *testing.T) { + want := tc.given + encoded := encoder.Encode(tc.given) + got := encoder.Decode(encoded) + assert.Equal(t, want, got) + assert.Equal(t, want, encoder.Stringify(got)) + }) + } +} diff --git a/x/common/asset/pair.go b/x/common/asset/pair.go index bb1a49ebe..09377ae51 100644 --- a/x/common/asset/pair.go +++ b/x/common/asset/pair.go @@ -83,6 +83,10 @@ func (pair Pair) QuoteDenom() string { // Validate performs a basic validation of the market params func (pair Pair) Validate() error { + if len(pair) == 0 { + return ErrInvalidTokenPair.Wrap("pair is empty") + } + split := strings.Split(pair.String(), ":") if len(split) != 2 { return ErrInvalidTokenPair.Wrap(pair.String()) diff --git a/x/common/asset/pair_test.go b/x/common/asset/pair_test.go index 6cce699d1..442f0f7ee 100644 --- a/x/common/asset/pair_test.go +++ b/x/common/asset/pair_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app/codec" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/app/codec" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" ) func TestTryNewPair(t *testing.T) { diff --git a/x/common/asset/registry.go b/x/common/asset/registry.go index 0db86b2d7..2dca04fab 100644 --- a/x/common/asset/registry.go +++ b/x/common/asset/registry.go @@ -1,8 +1,8 @@ package asset import ( - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/set" ) type registry map[string]set.Set[string] diff --git a/x/common/asset/registry_test.go b/x/common/asset/registry_test.go index abbf52f02..ad60df42e 100644 --- a/x/common/asset/registry_test.go +++ b/x/common/asset/registry_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" ) func TestIsSupportedPair(t *testing.T) { diff --git a/x/common/codec.go b/x/common/codec.go new file mode 100644 index 000000000..8bb5f7bf4 --- /dev/null +++ b/x/common/codec.go @@ -0,0 +1,149 @@ +package common + +import ( + "cosmossdk.io/collections" + collcodec "cosmossdk.io/collections/codec" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + // LegacyDecValue represents a collections.ValueCodec to work with LegacyDec. + LegacyDecValue collcodec.ValueCodec[sdkmath.LegacyDec] = legacyDecValueCodec{} + SdkIntKey collcodec.KeyCodec[sdkmath.Int] = mathIntKeyCodec{} + AccAddressValue collcodec.ValueCodec[sdk.AccAddress] = accAddressValueCodec{} +) + +// Collection Codecs + +// math.LegacyDec Value Codec + +type legacyDecValueCodec struct{} + +func (cdc legacyDecValueCodec) Encode(value sdkmath.LegacyDec) ([]byte, error) { + return value.Marshal() +} + +func (cdc legacyDecValueCodec) Decode(buffer []byte) (sdkmath.LegacyDec, error) { + v := sdkmath.LegacyZeroDec() + err := v.Unmarshal(buffer) + return v, err +} + +func (cdc legacyDecValueCodec) EncodeJSON(value sdkmath.LegacyDec) ([]byte, error) { + return value.MarshalJSON() +} + +func (cdc legacyDecValueCodec) DecodeJSON(buffer []byte) (sdkmath.LegacyDec, error) { + v := sdkmath.LegacyDec{} + err := v.UnmarshalJSON(buffer) + if err != nil { + return sdkmath.LegacyDec{}, err + } + return v, nil +} + +func (cdc legacyDecValueCodec) Stringify(value sdkmath.LegacyDec) string { + return value.String() +} + +func (cdc legacyDecValueCodec) ValueType() string { + return "math.LegacyDec" +} + +// AccAddress Value Codec + +type accAddressValueCodec struct{} + +func (cdc accAddressValueCodec) Encode(value sdk.AccAddress) ([]byte, error) { + return value.Marshal() +} + +func (cdc accAddressValueCodec) Decode(buffer []byte) (sdk.AccAddress, error) { + v := sdk.AccAddress{} + err := v.Unmarshal(buffer) + return v, err +} + +func (cdc accAddressValueCodec) EncodeJSON(value sdk.AccAddress) ([]byte, error) { + return value.MarshalJSON() +} + +func (cdc accAddressValueCodec) DecodeJSON(buffer []byte) (sdk.AccAddress, error) { + v := sdk.AccAddress{} + err := v.UnmarshalJSON(buffer) + if err != nil { + return nil, err + } + return v, nil +} + +func (cdc accAddressValueCodec) Stringify(value sdk.AccAddress) string { + return value.String() +} + +func (cdc accAddressValueCodec) ValueType() string { + return "sdk.AccAddress" +} + +// math.Int Key Codec + +type mathIntKeyCodec struct { + stringDecoder func(string) (sdkmath.Int, error) + keyType string +} + +func (cdc mathIntKeyCodec) Encode(buffer []byte, key sdkmath.Int) (int, error) { + bytes, err := key.Marshal() + if err != nil { + return 0, err + } + copy(bytes, buffer) + return key.Size(), nil +} + +func (cdc mathIntKeyCodec) Decode(buffer []byte) (int, sdkmath.Int, error) { + v := sdkmath.ZeroInt() + err := v.Unmarshal(buffer) + if err != nil { + return 0, v, err + } + return v.Size(), v, nil +} + +func (cdc mathIntKeyCodec) Size(key sdkmath.Int) int { + return key.Size() +} + +func (cdc mathIntKeyCodec) EncodeJSON(value sdkmath.Int) ([]byte, error) { + return collections.StringKey.EncodeJSON(value.String()) +} + +func (cdc mathIntKeyCodec) DecodeJSON(b []byte) (v sdkmath.Int, err error) { + s, err := collections.StringKey.DecodeJSON(b) + if err != nil { + return + } + v, err = cdc.stringDecoder(s) + return +} + +func (cdc mathIntKeyCodec) Stringify(key sdkmath.Int) string { + return key.String() +} + +func (cdc mathIntKeyCodec) KeyType() string { + return cdc.keyType +} + +func (cdc mathIntKeyCodec) EncodeNonTerminal(buffer []byte, key sdkmath.Int) (int, error) { + return cdc.Encode(buffer, key) +} + +func (cdc mathIntKeyCodec) DecodeNonTerminal(buffer []byte) (int, sdkmath.Int, error) { + return cdc.Decode(buffer) +} + +func (cdc mathIntKeyCodec) SizeNonTerminal(key sdkmath.Int) int { + return key.Size() +} diff --git a/x/common/constants.go b/x/common/constants.go index 9b0a36fdc..83376347d 100644 --- a/x/common/constants.go +++ b/x/common/constants.go @@ -1,7 +1,22 @@ package common +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + const ( TreasuryPoolModuleAccount = "treasury_pool" // TO_MICRO: multiplier for converting between units and micro-units. TO_MICRO = int64(1_000_000) + + NIBIRU_TEAM = "nibi1l8dxzwz9d4peazcqjclnkj2mhvtj7mpnkqx85mg0ndrlhwrnh7gskkzg0v" ) + +// TO_ATTO eth multiplier +var TO_ATTO = big.NewInt(1e18) + +func NibiruTeamAddr() sdk.AccAddress { + return sdk.MustAccAddressFromBech32(NIBIRU_TEAM) +} diff --git a/x/common/dec.go b/x/common/dec.go index 88ee73ee8..2a62e2c47 100644 --- a/x/common/dec.go +++ b/x/common/dec.go @@ -5,6 +5,7 @@ import ( "math/big" "strings" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -41,8 +42,8 @@ var ( // is available in the SqrtDec method. func MustSqrtDec(dec sdk.Dec) sdk.Dec { sqrtBigInt := MustSqrtBigInt(dec.BigInt()) - precision := sdk.NewDecFromBigInt(PRECISION_MULT) - return sdk.NewDecFromBigInt(sqrtBigInt).Quo(precision) + precision := math.LegacyNewDecFromBigInt(PRECISION_MULT) + return math.LegacyNewDecFromBigInt(sqrtBigInt).Quo(precision) } // SqrtDec computes the square root of the input decimal using its diff --git a/x/common/dec_test.go b/x/common/dec_test.go index a2a1d732e..5e79864b4 100644 --- a/x/common/dec_test.go +++ b/x/common/dec_test.go @@ -5,11 +5,12 @@ import ( "math/big" "testing" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" - "github.com/NibiruChain/nibiru/x/common" + "github.com/NibiruChain/nibiru/v2/x/common" ) func TestSqrtBigInt(t *testing.T) { @@ -48,17 +49,17 @@ func TestSqrtDec(t *testing.T) { }{ // -------------------------------------------------------------------- // Cases: 1 or higher - {dec: sdk.OneDec(), sqrtDec: sdk.OneDec()}, - {dec: sdk.NewDec(4), sqrtDec: sdk.NewDec(2)}, - {dec: sdk.NewDec(250_000), sqrtDec: sdk.NewDec(500)}, - {dec: sdk.NewDec(4_819_136_400), sqrtDec: sdk.NewDec(69_420)}, + {dec: math.LegacyOneDec(), sqrtDec: math.LegacyOneDec()}, + {dec: math.LegacyNewDec(4), sqrtDec: math.LegacyNewDec(2)}, + {dec: math.LegacyNewDec(250_000), sqrtDec: math.LegacyNewDec(500)}, + {dec: math.LegacyNewDec(4_819_136_400), sqrtDec: math.LegacyNewDec(69_420)}, // -------------------------------------------------------------------- // Cases: Between 0 and 1 - {dec: sdk.MustNewDecFromStr("0.81"), sqrtDec: sdk.MustNewDecFromStr("0.9")}, - {dec: sdk.MustNewDecFromStr("0.25"), sqrtDec: sdk.MustNewDecFromStr("0.5")}, + {dec: math.LegacyMustNewDecFromStr("0.81"), sqrtDec: math.LegacyMustNewDecFromStr("0.9")}, + {dec: math.LegacyMustNewDecFromStr("0.25"), sqrtDec: math.LegacyMustNewDecFromStr("0.5")}, // ↓ dec 1e-12, sqrtDec: 1e-6 - {dec: sdk.MustNewDecFromStr("0.000000000001"), sqrtDec: sdk.MustNewDecFromStr("0.000001")}, + {dec: math.LegacyMustNewDecFromStr("0.000000000001"), sqrtDec: math.LegacyMustNewDecFromStr("0.000001")}, // -------------------------------------------------------------------- // The math/big library panics if you call sqrt() on a negative number. @@ -66,7 +67,7 @@ func TestSqrtDec(t *testing.T) { t.Run("negative sqrt should panic", func(t *testing.T) { panicString := common.TryCatch(func() { - common.MustSqrtDec(sdk.NewDec(-9)) + common.MustSqrtDec(math.LegacyNewDec(-9)) })().Error() assert.Contains(t, panicString, "square root of negative number") @@ -138,21 +139,21 @@ func TestClamp(t *testing.T) { description string }{ { - value: sdk.NewDec(15), - clampValue: sdk.NewDec(1), - expected: sdk.NewDec(1), + value: math.LegacyNewDec(15), + clampValue: math.LegacyNewDec(1), + expected: math.LegacyNewDec(1), description: "Clamping 15 to 1", }, { - value: sdk.NewDec(-15), - clampValue: sdk.NewDec(1), - expected: sdk.NewDec(-1), + value: math.LegacyNewDec(-15), + clampValue: math.LegacyNewDec(1), + expected: math.LegacyNewDec(-1), description: "Clamping -15 to 1", }, { - value: sdk.MustNewDecFromStr("0.5"), - clampValue: sdk.NewDec(1), - expected: sdk.MustNewDecFromStr("0.5"), + value: math.LegacyMustNewDecFromStr("0.5"), + clampValue: math.LegacyNewDec(1), + expected: math.LegacyMustNewDecFromStr("0.5"), description: "Clamping 0.5 to 1", }, } diff --git a/x/common/error.go b/x/common/error.go index 8036faa77..de3e4acac 100644 --- a/x/common/error.go +++ b/x/common/error.go @@ -4,6 +4,9 @@ import ( "errors" "fmt" "runtime/debug" + + grpccodes "google.golang.org/grpc/codes" + grpcstatus "google.golang.org/grpc/status" ) // TryCatch is an implementation of the try-catch block from languages like C++ and JS. @@ -180,3 +183,13 @@ func CombineErrorsFromStrings(strs ...string) (err error) { } return CombineErrors(errs...) } + +var ErrNilGrpcMsg = grpcstatus.Errorf(grpccodes.InvalidArgument, "nil msg") + +// ErrNotImplemented: Represents an function error value. +func ErrNotImplemented() error { return fmt.Errorf("fn not implemented yet") } + +// ErrNotImplementedGprc: Represents an unimplemented gRPC method. +func ErrNotImplementedGprc() error { + return grpcstatus.Error(grpccodes.Unimplemented, ErrNotImplemented().Error()) +} diff --git a/x/common/error_test.go b/x/common/error_test.go index 264502981..058237633 100644 --- a/x/common/error_test.go +++ b/x/common/error_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" - "github.com/NibiruChain/nibiru/x/common" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" ) func newErrors(strs ...string) []error { diff --git a/x/common/ewma/ewma.go b/x/common/ewma/ewma.go index efc1a9137..7967d32e5 100644 --- a/x/common/ewma/ewma.go +++ b/x/common/ewma/ewma.go @@ -1,6 +1,9 @@ package ewma -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) type MovingAverage interface { Add(sdk.Dec) @@ -10,8 +13,8 @@ type MovingAverage interface { func NewMovingAverage(span sdk.Dec) MovingAverage { return &variableEWMA{ - value: sdk.ZeroDec(), - decay: sdk.MustNewDecFromStr("2").Quo(span.Add(sdk.OneDec())), + value: math.LegacyZeroDec(), + decay: math.LegacyMustNewDecFromStr("2").Quo(span.Add(math.LegacyOneDec())), } } @@ -28,7 +31,7 @@ func (v *variableEWMA) Add(dec sdk.Dec) { } // val = val * (1 - decay) + dec * decay - v.value = v.value.Mul(sdk.OneDec().Sub(v.decay)).Add(dec.Mul(v.decay)) + v.value = v.value.Mul(math.LegacyOneDec().Sub(v.decay)).Add(dec.Mul(v.decay)) } func (v *variableEWMA) Value() sdk.Dec { diff --git a/x/common/ewma/ewma_test.go b/x/common/ewma/ewma_test.go index cd5d92dbc..98d9f9d55 100644 --- a/x/common/ewma/ewma_test.go +++ b/x/common/ewma/ewma_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" "github.com/stretchr/testify/require" ) @@ -21,7 +21,7 @@ func TestSimpleEWMA(t *testing.T) { _, err = reader.Read() require.NoError(t, err) - ewma := NewMovingAverage(sdk.MustNewDecFromStr("240")) + ewma := NewMovingAverage(math.LegacyMustNewDecFromStr("240")) for { record, err := reader.Read() @@ -30,12 +30,12 @@ func TestSimpleEWMA(t *testing.T) { } require.NoError(t, err) - ewma.Add(sdk.MustNewDecFromStr(record[1])) + ewma.Add(math.LegacyMustNewDecFromStr(record[1])) require.Equal( t, - sdk.MustNewDecFromStr(record[2]).Mul(sdk.MustNewDecFromStr("100000")).TruncateInt(), - ewma.Value().Mul(sdk.MustNewDecFromStr("100000")).TruncateInt(), - fmt.Sprintf("value in position %s: %s should be equal to %s", record[0], ewma.Value().Mul(sdk.MustNewDecFromStr("1000000")).TruncateInt().String(), record[2]), + math.LegacyMustNewDecFromStr(record[2]).Mul(math.LegacyMustNewDecFromStr("100000")).TruncateInt(), + ewma.Value().Mul(math.LegacyMustNewDecFromStr("100000")).TruncateInt(), + fmt.Sprintf("value in position %s: %s should be equal to %s", record[0], ewma.Value().Mul(math.LegacyMustNewDecFromStr("1000000")).TruncateInt().String(), record[2]), ) } } diff --git a/x/common/omap/impl.go b/x/common/omap/impl.go index d7832dcdd..60400eaee 100644 --- a/x/common/omap/impl.go +++ b/x/common/omap/impl.go @@ -1,7 +1,11 @@ package omap import ( - "github.com/NibiruChain/nibiru/x/common/asset" + "math/big" + + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/x/common/asset" ) func stringIsLess(a, b string) bool { @@ -9,12 +13,12 @@ func stringIsLess(a, b string) bool { } // --------------------------------------------------------------------------- -// OrderedMap[string, V]: OrderedMap_String +// SortedMap[string, V] // --------------------------------------------------------------------------- -func OrderedMap_String[V any](data map[string]V) OrderedMap[string, V] { - omap := OrderedMap[string, V]{} - return omap.BuildFrom(data, stringSorter{}) +func SortedMap_String[V any](data map[string]V) SortedMap[string, V] { + omap := SortedMap[string, V]{} + return *omap.BuildFrom(data, stringSorter{}) } // stringSorter is a Sorter implementation for keys of type string . It uses @@ -28,14 +32,14 @@ func (sorter stringSorter) Less(a, b string) bool { } // --------------------------------------------------------------------------- -// OrderedMap[asset.Pair, V]: OrderedMap_Pair +// SortedMap[asset.Pair, V] // --------------------------------------------------------------------------- -func OrderedMap_Pair[V any]( +func SortedMap_Pair[V any]( data map[asset.Pair]V, -) OrderedMap[asset.Pair, V] { - omap := OrderedMap[asset.Pair, V]{} - return omap.BuildFrom(data, pairSorter{}) +) SortedMap[asset.Pair, V] { + omap := SortedMap[asset.Pair, V]{} + return *omap.BuildFrom(data, pairSorter{}) } // pairSorter is a Sorter implementation for keys of type asset.Pair. It uses @@ -47,3 +51,26 @@ var _ Sorter[asset.Pair] = (*pairSorter)(nil) func (sorter pairSorter) Less(a, b asset.Pair) bool { return stringIsLess(a.String(), b.String()) } + +// --------------------------------------------------------------------------- +// SortedMap[gethcommon.Address, V] +// --------------------------------------------------------------------------- + +func SortedMap_EthAddress[V any]( + data map[gethcommon.Address]V, +) SortedMap[gethcommon.Address, V] { + return *new(SortedMap[gethcommon.Address, V]).BuildFrom( + data, addrSorter{}, + ) +} + +// addrSorter implements "omap.Sorter" for the "gethcommon.Address" type. +type addrSorter struct{} + +var _ Sorter[gethcommon.Address] = (*addrSorter)(nil) + +func (s addrSorter) Less(a, b gethcommon.Address) bool { + aInt := new(big.Int).SetBytes(a.Bytes()) + bInt := new(big.Int).SetBytes(b.Bytes()) + return aInt.Cmp(bInt) < 0 +} diff --git a/x/common/omap/omap.go b/x/common/omap/omap.go index d40a3ed3c..e282e6ad4 100644 --- a/x/common/omap/omap.go +++ b/x/common/omap/omap.go @@ -1,9 +1,8 @@ // Package omap defines a generic-based type for creating ordered maps. It -// exports a "Sorter" interface, allowing the creation of ordered maps with +// exports a "Sorter" interface, allowing the creation of sorted maps with // custom key and value types. // -// Specifically, omap supports ordered maps with keys of type string or -// asset.Pair and values of any type. See impl.go for examples. +// See impl.go for examples. // // ## Motivation // @@ -19,14 +18,13 @@ import ( "sort" ) -// OrderedMap is a wrapper struct around the built-in map that has guarantees +// SortedMap is a wrapper struct around the built-in map that has guarantees // about order because it sorts its keys with a custom sorter. It has a public -// API that mirrors that functionality of `map`. OrderedMap is built with +// API that mirrors that functionality of `map`. SortedMap is built with // generics, so it can hold various combinations of key-value types. -type OrderedMap[K comparable, V any] struct { - Data map[K]V +type SortedMap[K comparable, V any] struct { + data map[K]V orderedKeys []K - keyIndexMap map[K]int // useful for delete operation isOrdered bool sorter Sorter[K] } @@ -38,11 +36,39 @@ type Sorter[K any] interface { Less(a K, b K) bool } +// SorterLeq is true if a <= b implements "less than or equal" using "Less" +func SorterLeq[K comparable](sorter Sorter[K], a, b K) bool { + return sorter.Less(a, b) || a == b +} + +// Data returns a copy of the underlying map (unordered, unsorted) +func (om *SortedMap[K, V]) Data() map[K]V { + dataCopy := make(map[K]V, len(om.data)) + for k, v := range om.InternalData() { + dataCopy[k] = v + } + return dataCopy +} + +// InternalData returns the SortedMap's private map. +func (om *SortedMap[K, V]) InternalData() map[K]V { + return om.data +} + +func (om *SortedMap[K, V]) Get(key K) (val V, exists bool) { + val, exists = om.data[key] + return val, exists +} + // ensureOrder is a method on the OrderedMap that sorts the keys in the map // and rebuilds the index map. -func (om *OrderedMap[K, V]) ensureOrder() { - keys := make([]K, 0, len(om.Data)) - for key := range om.Data { +func (om *SortedMap[K, V]) ensureOrder() { + if om.isOrdered { + return + } + + keys := make([]K, 0, len(om.data)) + for key := range om.data { keys = append(keys, key) } @@ -53,20 +79,16 @@ func (om *OrderedMap[K, V]) ensureOrder() { sort.Slice(keys, lessFunc) om.orderedKeys = keys - om.keyIndexMap = make(map[K]int) - for idx, key := range om.orderedKeys { - om.keyIndexMap[key] = idx - } om.isOrdered = true } // BuildFrom is a method that builds an OrderedMap from a given map and a // sorter for the keys. This function is useful for creating new OrderedMap // types with typed keys. -func (om OrderedMap[K, V]) BuildFrom( +func (om *SortedMap[K, V]) BuildFrom( data map[K]V, sorter Sorter[K], -) OrderedMap[K, V] { - om.Data = data +) *SortedMap[K, V] { + om.data = data om.sorter = sorter om.ensureOrder() return om @@ -76,7 +98,7 @@ func (om OrderedMap[K, V]) BuildFrom( // to iterate over the map in a deterministic order. Using a channel here // makes it so that the iteration is done lazily rather loading the entire // map (OrderedMap.data) into memory and then iterating. -func (om OrderedMap[K, V]) Range() <-chan (K) { +func (om *SortedMap[K, V]) Range() <-chan (K) { iterChan := make(chan K) go func() { defer close(iterChan) @@ -89,18 +111,18 @@ func (om OrderedMap[K, V]) Range() <-chan (K) { } // Has checks whether a key exists in the map. -func (om OrderedMap[K, V]) Has(key K) bool { - _, exists := om.Data[key] +func (om *SortedMap[K, V]) Has(key K) bool { + _, exists := om.data[key] return exists } // Len returns the number of items in the map. -func (om OrderedMap[K, V]) Len() int { - return len(om.Data) +func (om *SortedMap[K, V]) Len() int { + return len(om.data) } // Keys returns a slice of the keys in their sorted order. -func (om *OrderedMap[K, V]) Keys() []K { +func (om *SortedMap[K, V]) Keys() []K { if !om.isOrdered { om.ensureOrder() } @@ -109,19 +131,59 @@ func (om *OrderedMap[K, V]) Keys() []K { // Set adds a key-value pair to the map, or updates the value if the key // already exists. It ensures the keys are ordered after the operation. -func (om *OrderedMap[K, V]) Set(key K, val V) { - om.Data[key] = val +func (om *SortedMap[K, V]) Set(key K, val V) { + _, exists := om.data[key] + om.data[key] = val + + if !exists { + lenBefore := len(om.orderedKeys) + + if lenBefore == 0 { + // If the map is empty, create it. There's no need to search. + om.orderedKeys = []K{key} + return + } + + // If the key is new, insert it to the correctly sorted position + // Binary search works here and is in the standard library. + idx := sort.Search(lenBefore, func(i int) bool { + return om.sorter.Less(key, om.orderedKeys[i]) + }) + + // Update om.orderedKeys + newSortedKeys := make([]K, lenBefore+1) + front, back := om.orderedKeys[:idx], om.orderedKeys[idx:] + copy(newSortedKeys[:idx], front) // front + newSortedKeys[idx] = key // middle + copy(newSortedKeys[idx+1:], back) // back + om.orderedKeys = newSortedKeys + } +} + +// Union combines new key-value pairs into the ordered map. +func (om *SortedMap[K, V]) Union(kvMap map[K]V) { + for key, val := range kvMap { + om.data[key] = val + } + om.isOrdered = false om.ensureOrder() // TODO perf: make this more efficient with a clever insert. } // Delete removes a key-value pair from the map if the key exists. -func (om *OrderedMap[K, V]) Delete(key K) { - idx, keyExists := om.keyIndexMap[key] - if keyExists { - delete(om.Data, key) - - orderedKeys := om.orderedKeys - orderedKeys = append(orderedKeys[:idx], orderedKeys[idx+1:]...) - om.orderedKeys = orderedKeys +func (om *SortedMap[K, V]) Delete(key K) { + if _, keyExists := om.data[key]; keyExists { + lenBeforeDelete := om.Len() + delete(om.data, key) + + // Remove the key from orderedKeys while preserving the order + idx := sort.Search(lenBeforeDelete, func(i int) bool { + return SorterLeq(om.sorter, key, om.orderedKeys[i]) + }) + + // Update om.orderedKeys, skipping the deleted key (om.orderedKeys[idx]) + newSortedKeys := make([]K, lenBeforeDelete-1) + copy(newSortedKeys[:idx], om.orderedKeys[:idx]) // front + copy(newSortedKeys[idx:], om.orderedKeys[idx+1:]) // middle + back + om.orderedKeys = newSortedKeys } } diff --git a/x/common/omap/omap_test.go b/x/common/omap/omap_test.go index d0a8c0cbc..04c6bec5e 100644 --- a/x/common/omap/omap_test.go +++ b/x/common/omap/omap_test.go @@ -2,18 +2,29 @@ package omap_test import ( "fmt" + "math/big" "sort" "testing" - "github.com/stretchr/testify/require" + gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/omap" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/omap" ) -// TestLenHasKeys checks the length of the ordered map and verifies if the map +type Suite struct { + suite.Suite +} + +func TestSuite_RunAll(t *testing.T) { + suite.Run(t, new(Suite)) +} + +// TestLenHasKeys checks the length of the sorted map and verifies if the map // contains certain keys. -func TestLenHasKeys(t *testing.T) { +func (s *Suite) TestLenHasKeys() { type HasCheck struct { key string has bool @@ -45,70 +56,129 @@ func TestLenHasKeys(t *testing.T) { } for idx, tc := range testCases { - t.Run(fmt.Sprintf("case-%d", idx), func(t *testing.T) { - om := omap.OrderedMap_String[int](tc.dataMap) + s.Run(fmt.Sprintf("case-%d", idx), func() { + om := omap.SortedMap_String(tc.dataMap) - require.Equal(t, tc.len, om.Len()) + s.Require().Equal(tc.len, om.Len()) - orderedKeys := om.Keys() - definitelyOrderedKeys := []string{} - definitelyOrderedKeys = append(definitelyOrderedKeys, orderedKeys...) - sort.Strings(definitelyOrderedKeys) + sortedKeys := om.Keys() + definitelySortedKeys := []string{} + definitelySortedKeys = append(definitelySortedKeys, sortedKeys...) + sort.Strings(definitelySortedKeys) - require.Equal(t, definitelyOrderedKeys, orderedKeys) + s.Equal(definitelySortedKeys, sortedKeys) idx := 0 for key := range om.Range() { - require.Equal(t, orderedKeys[idx], key) + s.Equal(sortedKeys[idx], key) idx++ } }) } } -// TestGetSetDelete checks the Get, Set, and Delete operations on the OrderedMap. -func TestGetSetDelete(t *testing.T) { - om := omap.OrderedMap_String[string](make(map[string]string)) - require.Equal(t, 0, om.Len()) - - om.Set("foo", "fooval") - require.True(t, om.Has("foo")) - require.Equal(t, 1, om.Len()) - - om.Delete("bar") // shouldn't cause any problems - om.Delete("foo") - require.False(t, om.Has("foo")) - require.Equal(t, 0, om.Len()) +// TestGetSetDelete checks the Get, Set, and Delete operations on the SortedMap. +func (s *Suite) TestGetSetDelete() { + s.Run("single element", func() { + om := omap.SortedMap_String(make(map[string]string)) + s.Require().Equal(0, om.Len()) + om.Set("foo", "fooval") + s.Require().True(om.Has("foo")) + s.Require().Equal(1, om.Len()) + + om.Delete("bar") // shouldn't cause any problems + om.Delete("foo") + s.Require().False(om.Has("foo")) + s.Require().Equal(0, om.Len()) + }) + + s.Run("multiple elements", func() { + om := omap.SortedMap_String(map[string]string{ + "0": "w", + "1": "x", + "2": "y", + "3": "z", + }) + s.Require().Equal(4, om.Len()) + + om.Set("11", "xx") + om.Set("22", "yy") + s.Require().Equal([]string{"0", "1", "11", "2", "22", "3"}, om.Keys()) + + om.Delete("2") // shouldn't cause any problems + s.Require().False(om.Has("2")) + s.Require().Equal(5, om.Len()) + + s.Require().Equal([]string{"0", "1", "11", "22", "3"}, om.Keys()) + om.Union(map[string]string{"222": "", "111": ""}) + s.Require().Equal([]string{"0", "1", "11", "111", "22", "222", "3"}, om.Keys()) + + for k, v := range om.Data() { + gotVal, exists := om.Get(k) + s.True(exists) + s.Require().Equal(v, gotVal) + } + }) } -// TestOrderedMap_Pair tests an OrderedMap where the key is an asset.Pair, a -// type that isn't built-in. -func TestOrderedMap_Pair(t *testing.T) { +// DummyValue is a blank struct to use as a placeholder in the maps for the +// generic value argument. +type DummyValue struct{} + +// TestPair tests an SortedMap where the key is an asset.Pair. +func (s *Suite) TestPair() { pairStrs := []string{ "abc:xyz", "abc:abc", "aaa:bbb", "xyz:xyz", "bbb:ccc", "xyz:abc", } - orderedKeyStrs := []string{} - orderedKeyStrs = append(orderedKeyStrs, pairStrs...) - sort.Strings(orderedKeyStrs) + sortedKeyStrs := []string{} + sortedKeyStrs = append(sortedKeyStrs, pairStrs...) + sort.Strings(sortedKeyStrs) - orderedKeys := asset.MustNewPairs(orderedKeyStrs...) + sortedKeys := asset.MustNewPairs(sortedKeyStrs...) pairs := asset.MustNewPairs(pairStrs...) - type ValueType struct{} - unorderedMap := make(map[asset.Pair]ValueType) + unsortedMap := make(map[asset.Pair]DummyValue) for _, pair := range pairs { - unorderedMap[pair] = ValueType{} + unsortedMap[pair] = DummyValue{} } - om := omap.OrderedMap_Pair[ValueType](unorderedMap) - require.Equal(t, 6, om.Len()) - require.EqualValues(t, orderedKeys, om.Keys()) - require.NotEqualValues(t, asset.PairsToStrings(orderedKeys), pairStrs) + om := omap.SortedMap_Pair(unsortedMap) + s.Require().Equal(6, om.Len()) + s.Require().EqualValues(sortedKeys, om.Keys()) + s.Require().NotEqualValues(asset.PairsToStrings(sortedKeys), pairStrs) var pairsFromLoop []asset.Pair for pair := range om.Range() { pairsFromLoop = append(pairsFromLoop, pair) } - require.EqualValues(t, orderedKeys, pairsFromLoop) - require.NotEqualValues(t, pairsFromLoop, pairs) + s.Require().EqualValues(sortedKeys, pairsFromLoop) + s.Require().NotEqualValues(pairsFromLoop, pairs) +} + +func (s *Suite) TestEthAddress() { + s.Run("basic sorting", func() { + var sortedKeys []gethcommon.Address + unsortedMap := make(map[gethcommon.Address]DummyValue) + + // Prepare unsorted test inputs + omapKeyInt64s := []int64{1, 0, 4, 6, 3, 2, 5} + var unsortedKeys []gethcommon.Address + for _, i := range omapKeyInt64s { + bigInt := big.NewInt(i) + key := gethcommon.BigToAddress(bigInt) + unsortedKeys = append(unsortedKeys, key) + unsortedMap[key] = DummyValue{} + } + + { + for _, i := range []int64{0, 1, 2, 3, 4, 5, 6} { + sortedKeys = append(sortedKeys, gethcommon.BigToAddress(big.NewInt(i))) + } + } + + // Use sorter Sort + om := omap.SortedMap_EthAddress(unsortedMap) + s.Require().EqualValues(sortedKeys, om.Keys()) + s.NotEqualValues(unsortedKeys, sortedKeys) + }) } diff --git a/x/common/paginate_test.go b/x/common/paginate_test.go index 84197393b..f3a3744be 100644 --- a/x/common/paginate_test.go +++ b/x/common/paginate_test.go @@ -6,7 +6,7 @@ import ( sdkquery "github.com/cosmos/cosmos-sdk/types/query" "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/x/common" + "github.com/NibiruChain/nibiru/v2/x/common" ) type paginateSuite struct { @@ -74,7 +74,7 @@ func (s *paginateSuite) TestParsePagination() { wantOffset: 99, }, } { - s.T().Run(tc.name, func(t *testing.T) { + s.Run(tc.name, func() { gotPageReq, gotPage, gotErr := common.ParsePagination(tc.pageReq) s.EqualValues(tc.wantPage, gotPage) diff --git a/x/common/set/set.go b/x/common/set/set.go index 6b05a9055..eb33ff4a7 100644 --- a/x/common/set/set.go +++ b/x/common/set/set.go @@ -34,3 +34,9 @@ func New[T comparable](strs ...T) Set[T] { } return set } + +func (set Set[T]) AddMulti(sMulti ...T) { + for _, s := range sMulti { + set[s] = struct{}{} + } +} diff --git a/x/common/testutil/action/account.go b/x/common/testutil/action/account.go index d681ae9e6..84a61708d 100644 --- a/x/common/testutil/action/account.go +++ b/x/common/testutil/action/account.go @@ -3,8 +3,8 @@ package action import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" - tokenfactorytypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/app" + inflationtypes "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) type fundAccount struct { @@ -16,18 +16,18 @@ func FundAccount(account sdk.AccAddress, amount sdk.Coins) Action { return &fundAccount{Account: account, Amount: amount} } -func (c fundAccount) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { - err := app.BankKeeper.MintCoins(ctx, tokenfactorytypes.ModuleName, c.Amount) +func (c fundAccount) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { + err := app.BankKeeper.MintCoins(ctx, inflationtypes.ModuleName, c.Amount) if err != nil { - return ctx, err, true + return ctx, err } - err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, tokenfactorytypes.ModuleName, c.Account, c.Amount) + err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, inflationtypes.ModuleName, c.Account, c.Amount) if err != nil { - return ctx, err, true + return ctx, err } - return ctx, nil, true + return ctx, nil } type fundModule struct { @@ -39,16 +39,16 @@ func FundModule(module string, amount sdk.Coins) Action { return fundModule{Module: module, Amount: amount} } -func (c fundModule) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { - err := app.BankKeeper.MintCoins(ctx, tokenfactorytypes.ModuleName, c.Amount) +func (c fundModule) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { + err := app.BankKeeper.MintCoins(ctx, inflationtypes.ModuleName, c.Amount) if err != nil { - return ctx, err, true + return ctx, err } - err = app.BankKeeper.SendCoinsFromModuleToModule(ctx, tokenfactorytypes.ModuleName, c.Module, c.Amount) + err = app.BankKeeper.SendCoinsFromModuleToModule(ctx, inflationtypes.ModuleName, c.Module, c.Amount) if err != nil { - return ctx, err, true + return ctx, err } - return ctx, nil, true + return ctx, nil } diff --git a/x/common/testutil/action/block.go b/x/common/testutil/action/block.go index 614ef2b8f..9b82c7f7f 100644 --- a/x/common/testutil/action/block.go +++ b/x/common/testutil/action/block.go @@ -7,19 +7,19 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/v2/app" ) type increaseBlockNumberBy struct { numBlocks int64 } -func (i increaseBlockNumberBy) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (i increaseBlockNumberBy) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { app.EndBlocker(ctx, types.RequestEndBlock{Height: ctx.BlockHeight()}) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + i.numBlocks) - return ctx, nil, true + return ctx, nil } // IncreaseBlockNumberBy increases the block number by the given number of blocks @@ -31,10 +31,10 @@ type increaseBlockTimeBy struct { seconds time.Duration } -func (i increaseBlockTimeBy) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (i increaseBlockTimeBy) Do(_ *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Second * i.seconds)) - return ctx, nil, true + return ctx, nil } func IncreaseBlockTimeBy(seconds time.Duration) Action { @@ -45,8 +45,8 @@ type setBlockTime struct { blockTime time.Time } -func (s setBlockTime) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { - return ctx.WithBlockTime(s.blockTime), nil, true +func (s setBlockTime) Do(_ *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { + return ctx.WithBlockTime(s.blockTime), nil } // SetBlockTime sets the block time to the given value @@ -58,8 +58,8 @@ type setBlockNumber struct { blockNumber int64 } -func (s setBlockNumber) Do(_ *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { - return ctx.WithBlockHeight(s.blockNumber), nil, true +func (s setBlockNumber) Do(_ *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { + return ctx.WithBlockHeight(s.blockNumber), nil } // SetBlockNumber sets the block number to the given value @@ -69,7 +69,7 @@ func SetBlockNumber(blockNumber int64) Action { type moveToNextBlock struct{} -func (m moveToNextBlock) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (m moveToNextBlock) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { app.EndBlock(types.RequestEndBlock{}) app.Commit() @@ -85,7 +85,7 @@ func (m moveToNextBlock) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, e return app.NewContext( false, newHeader, - ).WithBlockTime(newHeader.Time), nil, true + ).WithBlockTime(newHeader.Time), nil } func MoveToNextBlock() Action { @@ -96,7 +96,7 @@ type moveToNextBlockWithDuration struct { blockDuration time.Duration } -func (m moveToNextBlockWithDuration) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (m moveToNextBlockWithDuration) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { app.EndBlock(types.RequestEndBlock{Height: ctx.BlockHeight()}) app.Commit() @@ -112,7 +112,7 @@ func (m moveToNextBlockWithDuration) Do(app *app.NibiruApp, ctx sdk.Context) (sd return app.NewContext( false, newHeader, - ).WithBlockTime(newHeader.Time), nil, true + ).WithBlockTime(newHeader.Time), nil } func MoveToNextBlockWithDuration(blockDuration time.Duration) Action { @@ -125,7 +125,7 @@ type moveToNextBlockWithTime struct { blockTime time.Time } -func (m moveToNextBlockWithTime) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (m moveToNextBlockWithTime) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { app.EndBlock(types.RequestEndBlock{Height: ctx.BlockHeight()}) app.Commit() @@ -141,7 +141,7 @@ func (m moveToNextBlockWithTime) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Co return app.NewContext( false, newHeader, - ).WithBlockTime(newHeader.Time), nil, true + ).WithBlockTime(newHeader.Time), nil } func MoveToNextBlockWithTime(blockTime time.Time) Action { diff --git a/x/common/testutil/action/testcase.go b/x/common/testutil/action/testcase.go index 633888ce6..753669111 100644 --- a/x/common/testutil/action/testcase.go +++ b/x/common/testutil/action/testcase.go @@ -7,16 +7,16 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" sdk "github.com/cosmos/cosmos-sdk/types" ) -// `Action` is a type of operation or task that can be performed in the +// Action is a type of operation or task that can be performed in the // Nibiru application. type Action interface { - // `Do` is a specific implementation of the `Action`. When `Do` is called, + // Do is a specific implementation of the `Action`. When `Do` is called, // the action is performed and some feedback is provided about the action's // success. `Do` can mutate the app. // @@ -25,12 +25,17 @@ type Action interface { // - err: The error if one was raised. // - isMandatory: Whether an error should have been raised. Do(app *app.NibiruApp, ctx sdk.Context) ( - outCtx sdk.Context, err error, isMandatory bool, + outCtx sdk.Context, err error, ) } -func ActionResp(ctx sdk.Context, respErr error) (outCtx sdk.Context, err error, isMandatory bool) { - return ctx, respErr, false +// IsNotMandatory is a marker interface for actions that are not mandatory, and it does not stop the test when there is an error. +type IsNotMandatory interface { + IsNotMandatory() +} + +func ActionResp(ctx sdk.Context, respErr error) (outCtx sdk.Context, err error) { + return ctx, respErr } type TestCases []TestCase @@ -48,67 +53,79 @@ func TC(name string) TestCase { return TestCase{Name: name} } -func (t TestCase) Given(action ...Action) TestCase { - t.given = append(t.given, action...) - return t -} - -func (t TestCase) When(action ...Action) TestCase { - t.when = append(t.when, action...) - return t -} - -func (t TestCase) Then(action ...Action) TestCase { - t.then = append(t.then, action...) - return t -} - -type TestSuite struct { - t *testing.T - - testCases []TestCase +func (tc TestCase) Given(action ...Action) TestCase { + tc.given = append(tc.given, action...) + return tc } -func NewTestSuite(t *testing.T) *TestSuite { - return &TestSuite{t: t} +func (tc TestCase) When(action ...Action) TestCase { + tc.when = append(tc.when, action...) + return tc } -func (t *TestSuite) WithTestCases(testCase ...TestCase) *TestSuite { - t.testCases = append(t.testCases, testCase...) - return t +func (tc TestCase) Then(action ...Action) TestCase { + tc.then = append(tc.then, action...) + return tc } -func (t *TestSuite) Run() { - for _, testCase := range t.testCases { +func (tc TestCase) Run(t *testing.T) { + t.Run(tc.Name, func(t *testing.T) { app, ctx := testapp.NewNibiruTestAppAndContextAtTime(time.UnixMilli(0)) var err error - var isMandatory bool + var isNotMandatory bool - for _, action := range testCase.given { - ctx, err, isMandatory = action.Do(app, ctx) - if isMandatory { - require.NoError(t.t, err, "failed to execute given action: %s", testCase.Name) + for _, action := range tc.given { + _, isNotMandatory = action.(IsNotMandatory) + + ctx, err = action.Do(app, ctx) + if isNotMandatory { + assert.NoError(t, err, "failed to execute given action: %s", tc.Name) } else { - assert.NoError(t.t, err, "failed to execute given action: %s", testCase.Name) + require.NoError(t, err, "failed to execute given action: %s", tc.Name) } } - for _, action := range testCase.when { - ctx, err, isMandatory = action.Do(app, ctx) - if isMandatory { - require.NoError(t.t, err, "failed to execute when action: %s", testCase.Name) + for _, action := range tc.when { + _, isNotMandatory = action.(IsNotMandatory) + + ctx, err = action.Do(app, ctx) + if isNotMandatory { + assert.NoError(t, err, "failed to execute when action: %s", tc.Name) } else { - assert.NoError(t.t, err, "failed to execute when action: %s", testCase.Name) + require.NoError(t, err, "failed to execute when action: %s", tc.Name) } } - for _, action := range testCase.then { - ctx, err, isMandatory = action.Do(app, ctx) - if isMandatory { - require.NoError(t.t, err, "failed to execute then action: %s", testCase.Name) + for _, action := range tc.then { + _, isNotMandatory = action.(IsNotMandatory) + + ctx, err = action.Do(app, ctx) + if isNotMandatory { + assert.NoError(t, err, "failed to execute then action: %s", tc.Name) } else { - assert.NoError(t.t, err, "failed to execute then action: %s", testCase.Name) + require.NoError(t, err, "failed to execute then action: %s", tc.Name) } } + }) +} + +type TestSuite struct { + t *testing.T + + testCases []TestCase +} + +func NewTestSuite(t *testing.T) *TestSuite { + return &TestSuite{t: t} +} + +func (ts *TestSuite) WithTestCases(testCase ...TestCase) *TestSuite { + ts.testCases = append(ts.testCases, testCase...) + return ts +} + +func (ts *TestSuite) Run() { + for _, testCase := range ts.testCases { + testCase.Run(ts.t) } } diff --git a/x/common/testutil/assertion/balances.go b/x/common/testutil/assertion/balances.go index 9cf61ff34..e98d78d71 100644 --- a/x/common/testutil/assertion/balances.go +++ b/x/common/testutil/assertion/balances.go @@ -3,14 +3,16 @@ package assertion import ( "fmt" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/action" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/v2/app" ) -func AllBalancesEqual(account sdk.AccAddress, amount sdk.Coins) *allBalancesEqual { +func AllBalancesEqual(account sdk.AccAddress, amount sdk.Coins) action.Action { return &allBalancesEqual{Account: account, Amount: amount} } @@ -19,7 +21,7 @@ type allBalancesEqual struct { Amount sdk.Coins } -func (b allBalancesEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (b allBalancesEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { coins := app.BankKeeper.GetAllBalances(ctx, b.Account) if !coins.IsEqual(b.Amount) { return ctx, fmt.Errorf( @@ -27,13 +29,13 @@ func (b allBalancesEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, b.Account.String(), b.Amount.String(), coins.String(), - ), false + ) } - return ctx, nil, false + return ctx, nil } -func BalanceEqual(account sdk.AccAddress, denom string, amount sdkmath.Int) *balanceEqual { +func BalanceEqual(account sdk.AccAddress, denom string, amount sdkmath.Int) action.Action { return &balanceEqual{Account: account, Denom: denom, Amount: amount} } @@ -43,7 +45,9 @@ type balanceEqual struct { Amount sdkmath.Int } -func (b balanceEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (b balanceEqual) IsNotMandatory() {} + +func (b balanceEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { coin := app.BankKeeper.GetBalance(ctx, b.Account, b.Denom) if !coin.Amount.Equal(b.Amount) { return ctx, fmt.Errorf( @@ -51,13 +55,13 @@ func (b balanceEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, erro b.Account.String(), b.Amount.String(), coin.String(), - ), false + ) } - return ctx, nil, false + return ctx, nil } -func ModuleBalanceEqual(moduleName string, denom string, amount sdkmath.Int) *moduleBalanceEqual { +func ModuleBalanceEqual(moduleName string, denom string, amount sdkmath.Int) action.Action { return &moduleBalanceEqual{ModuleName: moduleName, Denom: denom, Amount: amount} } @@ -67,7 +71,9 @@ type moduleBalanceEqual struct { Amount sdkmath.Int } -func (b moduleBalanceEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (b moduleBalanceEqual) IsNotMandatory() {} + +func (b moduleBalanceEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { coin := app.BankKeeper.GetBalance(ctx, app.AccountKeeper.GetModuleAddress(b.ModuleName), b.Denom) if !coin.Amount.Equal(b.Amount) { return ctx, fmt.Errorf( @@ -75,8 +81,8 @@ func (b moduleBalanceEqual) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context b.ModuleName, b.Amount.String(), coin.String(), - ), false + ) } - return ctx, nil, false + return ctx, nil } diff --git a/x/common/testutil/assertion/gas.go b/x/common/testutil/assertion/gas.go index 960d87be6..55cd4c5d9 100644 --- a/x/common/testutil/assertion/gas.go +++ b/x/common/testutil/assertion/gas.go @@ -5,21 +5,21 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/action" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/action" ) type gasConsumedShouldBe struct { gasConsumed uint64 } -func (g gasConsumedShouldBe) Do(_ *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (g gasConsumedShouldBe) Do(_ *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { gasUsed := ctx.GasMeter().GasConsumed() if g.gasConsumed != gasUsed { - return ctx, fmt.Errorf("gas consumed should be %d, but got %d", g.gasConsumed, gasUsed), true + return ctx, fmt.Errorf("gas consumed should be %d, but got %d", g.gasConsumed, gasUsed) } - return ctx, nil, true + return ctx, nil } func GasConsumedShouldBe(gasConsumed uint64) action.Action { diff --git a/x/common/testutil/cases.go b/x/common/testutil/cases.go index 547738d00..8ef1dc313 100644 --- a/x/common/testutil/cases.go +++ b/x/common/testutil/cases.go @@ -1,6 +1,7 @@ package testutil import ( + "strings" "testing" ) @@ -34,3 +35,40 @@ func BeforeIntegrationSuite(suiteT *testing.T) { } suiteT.Log("setting up integration test suite") } + +// RetrySuiteRunIfDbClosed runs a test suite with retries, recovering from a +// specific panic message, "pebbledb: closed" that often surfaces in CI when tests +// involve "Nibiru/x/common/testutil/testnetwork". +// For full context, see https://github.com/NibiruChain/nibiru/issues/1918. +func RetrySuiteRunIfDbClosed(t *testing.T, runTest func(), maxRetries int) { + panicMessage := "pebbledb: closed" + for attempt := 0; attempt < maxRetries; attempt++ { + panicked := false + + func() { + defer func() { + if r := recover(); r != nil { + if errMsg, ok := r.(string); ok && strings.Contains(errMsg, panicMessage) { + t.Logf("Recovered from panic on attempt %d: %v", attempt, r) + panicked = true + } else { + panic(r) // Re-panic if it's not the specific error + } + } + }() + + // Run the test suite + runTest() + // suite.Run(t, suiteInstance) + }() + + if !panicked { + t.Logf("Test suite succeeded on attempt %d", attempt) + return + } + + t.Logf("Retrying test suite: attempt %d", attempt+1) + } + + t.Fatalf("Test suite failed after %d attempts due to '%s'", maxRetries, panicMessage) +} diff --git a/x/common/testutil/cases_test.go b/x/common/testutil/cases_test.go new file mode 100644 index 000000000..fbaa72a51 --- /dev/null +++ b/x/common/testutil/cases_test.go @@ -0,0 +1,29 @@ +package testutil_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil" +) + +func TestRunFunctionTests(t *testing.T) { + testutil.RunFunctionTests(t, testutil.FunctionTestCases{ + { + Name: "test case A", + Test: func() { + }, + }, + }) +} + +func TestBeforeIntegrationSuite(t *testing.T) { + testutil.BeforeIntegrationSuite(t) + + if testing.Short() { + require.True(t, t.Skipped()) + } else { + require.False(t, t.Skipped()) + } +} diff --git a/x/common/testutil/cli/logger.go b/x/common/testutil/cli/logger.go deleted file mode 100644 index 0a55a913e..000000000 --- a/x/common/testutil/cli/logger.go +++ /dev/null @@ -1,14 +0,0 @@ -package cli - -import ( - "testing" -) - -// Logger is a network logger interface that exposes testnet-level Log() methods for an in-process testing network -// This is not to be confused with logging that may happen at an individual node or validator level -type Logger interface { - Log(args ...interface{}) - Logf(format string, args ...interface{}) -} - -var _ Logger = (*testing.T)(nil) diff --git a/x/common/testutil/events.go b/x/common/testutil/events.go index 020a09af5..74408dd8b 100644 --- a/x/common/testutil/events.go +++ b/x/common/testutil/events.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/gogoproto/proto" - "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/set" abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/common/testutil/events_test.go b/x/common/testutil/events_test.go index 5b3b69a16..85f612c85 100644 --- a/x/common/testutil/events_test.go +++ b/x/common/testutil/events_test.go @@ -3,9 +3,9 @@ package testutil_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" ) func (s *TestSuite) TestEventsUtils() { diff --git a/x/common/testutil/genesis/genesis.go b/x/common/testutil/genesis/genesis.go index 70837d526..fb0f30fd0 100644 --- a/x/common/testutil/genesis/genesis.go +++ b/x/common/testutil/genesis/genesis.go @@ -7,9 +7,9 @@ import ( gov "github.com/cosmos/cosmos-sdk/x/gov/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" ) /* @@ -19,7 +19,7 @@ genesis as input. The blockchain genesis state is represented as a map from modu identifier strings to raw json messages. */ func NewTestGenesisState(encodingConfig app.EncodingConfig) app.GenesisState { - codec := encodingConfig.Marshaler + codec := encodingConfig.Codec genState := app.NewDefaultGenesisState(codec) // Set short voting period to allow fast gov proposals in tests diff --git a/x/common/testutil/genesis/oracle_genesis.go b/x/common/testutil/genesis/oracle_genesis.go index 41cae95fd..77faea59b 100644 --- a/x/common/testutil/genesis/oracle_genesis.go +++ b/x/common/testutil/genesis/oracle_genesis.go @@ -1,23 +1,27 @@ package genesis import ( - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - oracletypes "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + oracletypes "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func AddOracleGenesis(gen app.GenesisState) app.GenesisState { + gen[oracletypes.ModuleName] = app.MakeEncodingConfig().Codec. + MustMarshalJSON(OracleGenesis()) + return gen +} + +func OracleGenesis() *oracletypes.GenesisState { oracleGenesis := oracletypes.DefaultGenesisState() oracleGenesis.ExchangeRates = []oracletypes.ExchangeRateTuple{ - {Pair: asset.Registry.Pair(denoms.ETH, denoms.NUSD), ExchangeRate: sdk.NewDec(1_000)}, - {Pair: asset.Registry.Pair(denoms.NIBI, denoms.NUSD), ExchangeRate: sdk.NewDec(10)}, + {Pair: asset.Registry.Pair(denoms.ETH, denoms.NUSD), ExchangeRate: math.LegacyNewDec(1_000)}, + {Pair: asset.Registry.Pair(denoms.NIBI, denoms.NUSD), ExchangeRate: math.LegacyNewDec(10)}, } oracleGenesis.Params.VotePeriod = 1_000 - gen[oracletypes.ModuleName] = app.MakeEncodingConfig().Marshaler. - MustMarshalJSON(oracleGenesis) - return gen + return oracleGenesis } diff --git a/x/common/testutil/genesis/sudo_genesis.go b/x/common/testutil/genesis/sudo_genesis.go index ce1a08c70..84771dd83 100644 --- a/x/common/testutil/genesis/sudo_genesis.go +++ b/x/common/testutil/genesis/sudo_genesis.go @@ -3,14 +3,14 @@ package genesis import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/v2/app" - "github.com/NibiruChain/nibiru/x/sudo" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" ) func AddSudoGenesis(gen app.GenesisState) ( @@ -19,7 +19,7 @@ func AddSudoGenesis(gen app.GenesisState) ( rootAddr sdk.AccAddress, ) { sudoGenesis, rootPrivKey, rootAddr := SudoGenesis() - gen[sudotypes.ModuleName] = app.MakeEncodingConfig().Marshaler. + gen[sudotypes.ModuleName] = app.MakeEncodingConfig().Codec. MustMarshalJSON(sudoGenesis) return gen, rootPrivKey, rootAddr } diff --git a/x/common/testutil/nullify.go b/x/common/testutil/nullify.go index 202b59e6f..96fc340f6 100644 --- a/x/common/testutil/nullify.go +++ b/x/common/testutil/nullify.go @@ -16,7 +16,7 @@ var ( // Fill analyze all struct fields and slices with // reflection and initialize the nil and empty slices, // structs, and pointers. -func Fill(x interface{}) interface{} { +func Fill(x any) any { v := reflect.Indirect(reflect.ValueOf(x)) switch v.Kind() { case reflect.Slice: diff --git a/x/common/testutil/testapp/test_util.go b/x/common/testutil/testapp/test_util.go index 9ff0b7fb4..d94318da1 100644 --- a/x/common/testutil/testapp/test_util.go +++ b/x/common/testutil/testapp/test_util.go @@ -3,6 +3,7 @@ package testapp import ( "time" + "cosmossdk.io/math" tmtypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -14,7 +15,8 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - nibiruapp "github.com/NibiruChain/nibiru/app" + nibiruapp "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/appconst" ) // GenesisStateWithSingleValidator initializes GenesisState with a single validator and genesis accounts @@ -40,7 +42,7 @@ func GenesisStateWithSingleValidator(codec codec.Codec, genesisState nibiruapp.G balances = append(balances, banktypes.Balance{ Address: acc.GetAddress().String(), - Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + Coins: sdk.NewCoins(sdk.NewCoin(appconst.BondDenom, math.NewIntFromUint64(1e14))), }) genesisState, err = genesisStateWithValSet(codec, genesisState, valSet, []authtypes.GenesisAccount{acc}, balances...) @@ -81,18 +83,20 @@ func genesisStateWithValSet( Jailed: false, Status: stakingtypes.Bonded, Tokens: bondAmt, - DelegatorShares: sdk.OneDec(), + DelegatorShares: math.LegacyOneDec(), Description: stakingtypes.Description{}, UnbondingHeight: int64(0), UnbondingTime: time.Unix(0, 0).UTC(), - Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), - MinSelfDelegation: sdk.ZeroInt(), + Commission: stakingtypes.NewCommission(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()), + MinSelfDelegation: math.ZeroInt(), } validators = append(validators, validator) - delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), math.LegacyOneDec())) } // set validators and delegations - stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) + stakingParams := stakingtypes.DefaultParams() + stakingParams.BondDenom = appconst.BondDenom + stakingGenesis := stakingtypes.NewGenesisState(stakingParams, validators, delegations) genesisState[stakingtypes.ModuleName] = cdc.MustMarshalJSON(stakingGenesis) totalSupply := sdk.NewCoins() @@ -103,13 +107,13 @@ func genesisStateWithValSet( for range delegations { // add delegated tokens to total supply - totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) + totalSupply = totalSupply.Add(sdk.NewCoin(appconst.BondDenom, bondAmt)) } // add bonded amount to bonded pool module account balances = append(balances, banktypes.Balance{ Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), - Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, + Coins: sdk.Coins{sdk.NewCoin(appconst.BondDenom, bondAmt)}, }) // update total supply diff --git a/x/common/testutil/testapp/testapp.go b/x/common/testutil/testapp/testapp.go index cece042c9..24f39951c 100644 --- a/x/common/testutil/testapp/testapp.go +++ b/x/common/testutil/testapp/testapp.go @@ -4,21 +4,26 @@ import ( "encoding/json" "time" + "cosmossdk.io/math" tmdb "github.com/cometbft/cometbft-db" abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/testutil" - epochstypes "github.com/NibiruChain/nibiru/x/epochs/types" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" - tokenfactorytypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/appconst" + cryptocodec "github.com/NibiruChain/nibiru/v2/eth/crypto/codec" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + epochstypes "github.com/NibiruChain/nibiru/v2/x/epochs/types" + inflationtypes "github.com/NibiruChain/nibiru/v2/x/inflation/types" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) func init() { @@ -28,34 +33,48 @@ func init() { // NewNibiruTestAppAndContext creates an 'app.NibiruApp' instance with an // in-memory 'tmdb.MemDB' and fresh 'sdk.Context'. func NewNibiruTestAppAndContext() (*app.NibiruApp, sdk.Context) { + // Prevent "invalid Bech32 prefix; expected nibi, got ...." error + EnsureNibiruPrefix() + + // Set up base app encoding := app.MakeEncodingConfig() - appGenesis := app.NewDefaultGenesisState(encoding.Marshaler) + var appGenesis app.GenesisState = app.NewDefaultGenesisState(encoding.Codec) genModEpochs := epochstypes.DefaultGenesisFromTime(time.Now().UTC()) // Set happy genesis: epochs - appGenesis[epochstypes.ModuleName] = encoding.Marshaler.MustMarshalJSON( + appGenesis[epochstypes.ModuleName] = encoding.Codec.MustMarshalJSON( genModEpochs, ) // Set happy genesis: sudo sudoGenesis := new(sudotypes.GenesisState) sudoGenesis.Sudoers = DefaultSudoers() - appGenesis[sudotypes.ModuleName] = encoding.Marshaler.MustMarshalJSON(sudoGenesis) + appGenesis[sudotypes.ModuleName] = encoding.Codec.MustMarshalJSON(sudoGenesis) app := NewNibiruTestApp(appGenesis) ctx := NewContext(app) - app.OracleKeeper.SetPrice(ctx, asset.Registry.Pair(denoms.BTC, denoms.NUSD), sdk.NewDec(20000)) - app.OracleKeeper.SetPrice(ctx, "xxx:yyy", sdk.NewDec(20000)) + // Set defaults for certain modules. + app.OracleKeeper.SetPrice(ctx, asset.Registry.Pair(denoms.BTC, denoms.NUSD), math.LegacyNewDec(20000)) + app.OracleKeeper.SetPrice(ctx, "xxx:yyy", math.LegacyNewDec(20000)) + app.SudoKeeper.Sudoers.Set(ctx, DefaultSudoers()) return app, ctx } +// NewContext: Returns a fresh sdk.Context corresponding to the given NibiruApp. func NewContext(nibiru *app.NibiruApp) sdk.Context { - return nibiru.NewContext(false, tmproto.Header{ + blockHeader := tmproto.Header{ Height: 1, Time: time.Now().UTC(), - }) + } + ctx := nibiru.NewContext(false, blockHeader) + + // Make sure there's a block proposer on the context. + blockHeader.ProposerAddress = FirstBlockProposer(nibiru, ctx) + ctx = ctx.WithBlockHeader(blockHeader) + + return ctx } // DefaultSudoers: State for the x/sudo module for the default test app. @@ -71,15 +90,24 @@ func DefaultSudoRoot() sdk.AccAddress { return sdk.MustAccAddressFromBech32(testutil.ADDR_SUDO_ROOT) } +func FirstBlockProposer( + chain *app.NibiruApp, ctx sdk.Context, +) (proposerAddr sdk.ConsAddress) { + maxQueryCount := uint32(10) + valopers := chain.StakingKeeper.GetValidators(ctx, maxQueryCount) + valAddrBz := valopers[0].GetOperator().Bytes() + return sdk.ConsAddress(valAddrBz) +} + // SetDefaultSudoGenesis: Sets the sudo module genesis state to a valid // default. See "DefaultSudoers". func SetDefaultSudoGenesis(gen app.GenesisState) { sudoGen := new(sudotypes.GenesisState) encoding := app.MakeEncodingConfig() - encoding.Marshaler.MustUnmarshalJSON(gen[sudotypes.ModuleName], sudoGen) + encoding.Codec.MustUnmarshalJSON(gen[sudotypes.ModuleName], sudoGen) if err := sudoGen.Validate(); err != nil { sudoGen.Sudoers = DefaultSudoers() - gen[sudotypes.ModuleName] = encoding.Marshaler.MustMarshalJSON(sudoGen) + gen[sudotypes.ModuleName] = encoding.Codec.MustMarshalJSON(sudoGen) } } @@ -94,11 +122,12 @@ func NewNibiruTestAppAndContextAtTime(startTime time.Time) (*app.NibiruApp, sdk. // NewNibiruTestApp initializes a chain with the given genesis state to // creates an application instance ('app.NibiruApp'). This app uses an // in-memory database ('tmdb.MemDB') and has logging disabled. -func NewNibiruTestApp(gen app.GenesisState) *app.NibiruApp { +func NewNibiruTestApp(gen app.GenesisState, baseAppOptions ...func(*baseapp.BaseApp)) *app.NibiruApp { db := tmdb.NewMemDB() logger := log.NewNopLogger() encoding := app.MakeEncodingConfig() + cryptocodec.RegisterInterfaces(encoding.InterfaceRegistry) SetDefaultSudoGenesis(gen) app := app.NewNibiruApp( @@ -108,9 +137,10 @@ func NewNibiruTestApp(gen app.GenesisState) *app.NibiruApp { /*loadLatest=*/ true, encoding, /*appOpts=*/ sims.EmptyAppOptions{}, + baseAppOptions..., ) - gen, err := GenesisStateWithSingleValidator(encoding.Marshaler, gen) + gen, err := GenesisStateWithSingleValidator(encoding.Codec, gen) if err != nil { panic(err) } @@ -135,11 +165,11 @@ func FundAccount( bankKeeper bankkeeper.Keeper, ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins, ) error { - if err := bankKeeper.MintCoins(ctx, tokenfactorytypes.ModuleName, amounts); err != nil { + if err := bankKeeper.MintCoins(ctx, inflationtypes.ModuleName, amounts); err != nil { return err } - return bankKeeper.SendCoinsFromModuleToAccount(ctx, tokenfactorytypes.ModuleName, addr, amounts) + return bankKeeper.SendCoinsFromModuleToAccount(ctx, inflationtypes.ModuleName, addr, amounts) } // FundModuleAccount is a utility function that funds a module account by @@ -149,11 +179,24 @@ func FundModuleAccount( bankKeeper bankkeeper.Keeper, ctx sdk.Context, recipientMod string, amounts sdk.Coins, ) error { - if err := bankKeeper.MintCoins(ctx, tokenfactorytypes.ModuleName, amounts); err != nil { + if err := bankKeeper.MintCoins(ctx, inflationtypes.ModuleName, amounts); err != nil { return err } - return bankKeeper.SendCoinsFromModuleToModule(ctx, tokenfactorytypes.ModuleName, recipientMod, amounts) + return bankKeeper.SendCoinsFromModuleToModule(ctx, inflationtypes.ModuleName, recipientMod, amounts) +} + +// FundFeeCollector funds the module account that collects gas fees with some +// amount of "unibi", the gas token. +func FundFeeCollector( + bk bankkeeper.Keeper, ctx sdk.Context, amount math.Int, +) error { + return FundModuleAccount( + bk, + ctx, + auth.FeeCollectorName, + sdk.NewCoins(sdk.NewCoin(appconst.BondDenom, amount)), + ) } // EnsureNibiruPrefix sets the account address prefix to Nibiru's rather than @@ -161,7 +204,7 @@ func FundModuleAccount( // addresses rather than cosmos ones (for Gaia). func EnsureNibiruPrefix() { csdkConfig := sdk.GetConfig() - nibiruPrefix := app.AccountAddressPrefix + nibiruPrefix := appconst.AccountAddressPrefix if csdkConfig.GetBech32AccountAddrPrefix() != nibiruPrefix { app.SetPrefixes(nibiruPrefix) } diff --git a/x/common/testutil/cli/doc.go b/x/common/testutil/testnetwork/doc.go similarity index 95% rename from x/common/testutil/cli/doc.go rename to x/common/testutil/testnetwork/doc.go index be9e84425..3467e73d6 100644 --- a/x/common/testutil/cli/doc.go +++ b/x/common/testutil/testnetwork/doc.go @@ -1,5 +1,5 @@ /* -Package network implements and exposes a fully operational in-process Tendermint +Package "testnetwork" implements and exposes a fully operational in-process Tendermint test network that consists of at least one or potentially many validators. This test network can be used primarily for integration tests or unit test suites. @@ -62,4 +62,4 @@ A typical testing flow might look like the following: suite.Run(t, new(IntegrationTestSuite)) } */ -package cli +package testnetwork diff --git a/x/common/testutil/testnetwork/logger.go b/x/common/testutil/testnetwork/logger.go new file mode 100644 index 000000000..7b1206a94 --- /dev/null +++ b/x/common/testutil/testnetwork/logger.go @@ -0,0 +1,18 @@ +package testnetwork + +import ( + "testing" +) + +// Logger is a network logger interface that exposes testnet-level Log() methods +// for an in-process testing network This is not to be confused with logging that +// may happen at an individual node or validator level +// +// Typically, a `testing.T` struct is used as the logger for both the "Network" +// and corresponding "Validators". +type Logger interface { + Log(args ...any) + Logf(format string, args ...any) +} + +var _ Logger = (*testing.T)(nil) diff --git a/x/common/testutil/cli/network.go b/x/common/testutil/testnetwork/network.go similarity index 68% rename from x/common/testutil/cli/network.go rename to x/common/testutil/testnetwork/network.go index 897f286a5..6cdc13feb 100644 --- a/x/common/testutil/cli/network.go +++ b/x/common/testutil/testnetwork/network.go @@ -1,4 +1,4 @@ -package cli +package testnetwork import ( "bufio" @@ -6,45 +6,44 @@ import ( "encoding/json" "errors" "fmt" - "net/http" "net/url" "os" "path/filepath" - "strings" "sync" "time" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + serverconfig "github.com/NibiruChain/nibiru/v2/app/server/config" + "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/store/pruning/types" "github.com/cosmos/cosmos-sdk/testutil/sims" + "cosmossdk.io/math" dbm "github.com/cometbft/cometbft-db" "github.com/cosmos/cosmos-sdk/baseapp" - sdktestutil "github.com/cosmos/cosmos-sdk/testutil" tmrand "github.com/cometbft/cometbft/libs/rand" - "github.com/cometbft/cometbft/node" - tmclient "github.com/cometbft/cometbft/rpc/client" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/server" - serverapi "github.com/cosmos/cosmos-sdk/server/api" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "google.golang.org/grpc" - "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" - "github.com/NibiruChain/nibiru/app" + "github.com/NibiruChain/nibiru/v2/app" ) // package-wide network lock to only allow one test network at a time @@ -54,85 +53,27 @@ var lock = new(sync.Mutex) // creates an ABCI Application to provide to Tendermint. type AppConstructor = func(val Validator) servertypes.Application -type ( - // Network defines a in-process testing network. It is primarily intended - // for client and integration testing. The Network struct can spawn any - // number of validators, each with its own RPC and API clients. - // - // ### Constraints - // - // 1. Only the first validator will have a functional RPC and API - // server/client. - // 2. Due to constraints in Tendermint's JSON-RPC implementation, only one - // test network can run at a time. For this reason, it's essential to - // invoke `Network.Cleanup` after testing to allow other tests to create - // networks. - Network struct { - BaseDir string - Config Config - Validators []*Validator - Logger Logger - } - - // Validator defines an in-process Tendermint validator node. Through this - // object, a client can make RPC and API calls and interact with any client - // command or handler. - Validator struct { - AppConfig *serverconfig.Config - ClientCtx client.Context - Ctx *server.Context - // Dir is the root directory of the validator node data and config. Passed to the Tendermint config. - Dir string - - // NodeID is a unique ID for the validator generated when the - // 'cli.Network' is started. - NodeID string - PubKey cryptotypes.PubKey - - // Moniker is a human-readable name that identifies a validator. A - // moniker is optional and may be empty. - Moniker string - - // APIAddress is the endpoint that the validator API server binds to. - // Only the first validator of a 'cli.Network' exposes the full API. - APIAddress string - - // RPCAddress is the endpoint that the RPC server binds to. Only the - // first validator of a 'cli.Network' exposes the full API. - RPCAddress string - - // P2PAddress is the endpoint that the RPC server binds to. The P2P - // server handles Tendermint peer-to-peer (P2P) networking and is - // critical for blockchain replication and consensus. It allows nodes - // to gossip blocks, transactions, and consensus messages. Only the - // first validator of a 'cli.Network' exposes the full API. - P2PAddress string - - // Address - account address - Address sdk.AccAddress - - // ValAddress - validator operator (valoper) address - ValAddress sdk.ValAddress - - // RPCClient wraps most important rpc calls a client would make to - // listen for events, test if it also implements events.EventSwitch. - // - // RPCClient implementations in "github.com/cometbft/cometbft/rpc" v0.37.2: - // - rcp.HTTP - // - rpc.Local - RPCClient tmclient.Client - - tmNode *node.Node - - // API exposes the app's REST and gRPC interfaces, allowing clients to - // read from state and broadcast txs. The API server connects to the - // underlying ABCI application. - api *serverapi.Server - grpc *grpc.Server - grpcWeb *http.Server - secretMnemonic string - } -) +// Network defines an in-process testing network. It is primarily intended +// for client and integration testing. The Network struct can spawn any +// number of validators, each with its own RPC and API clients. +// +// ### Constraints +// +// 1. Only the first validator will have a functional RPC and API +// server/client. +// 2. Due to constraints in Tendermint's JSON-RPC implementation, only one +// test network can run at a time. For this reason, it's essential to +// invoke `Network.Cleanup` after testing to allow other tests to create +// networks. +// +// Each of the "Validators" has a "Logger", each being a shared reference to the +// `Network.Logger`. This helps simplify debugging. +type Network struct { + BaseDir string + Config Config + Validators []*Validator + Logger Logger +} // NewAppConstructor returns a new simapp AppConstructor func NewAppConstructor(encodingCfg app.EncodingConfig, chainID string) AppConstructor { @@ -157,40 +98,67 @@ func BuildNetworkConfig(appGenesis app.GenesisState) Config { chainID := "chain-" + tmrand.NewRand().Str(6) return Config{ - Codec: encCfg.Marshaler, - TxConfig: encCfg.TxConfig, - LegacyAmino: encCfg.Amino, - InterfaceRegistry: encCfg.InterfaceRegistry, AccountRetriever: authtypes.AccountRetriever{}, + AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction), AppConstructor: NewAppConstructor(encCfg, chainID), - GenesisState: appGenesis, - TimeoutCommit: time.Second / 2, - ChainID: chainID, - NumValidators: 1, BondDenom: denoms.NIBI, + BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction), + ChainID: chainID, + CleanupDir: true, + Codec: encCfg.Codec, + EnableTMLogging: false, // super noisy + GenesisState: appGenesis, + InterfaceRegistry: encCfg.InterfaceRegistry, + KeyringOptions: []keyring.Option{}, + LegacyAmino: encCfg.Amino, MinGasPrices: fmt.Sprintf("0.000006%s", denoms.NIBI), - AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction), + NumValidators: 1, + PruningStrategy: types.PruningOptionNothing, + SigningAlgo: string(hd.Secp256k1Type), StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction), - BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction), StartingTokens: sdk.NewCoins( sdk.NewCoin(denoms.NUSD, sdk.TokensFromConsensusPower(1e12, sdk.DefaultPowerReduction)), sdk.NewCoin(denoms.NIBI, sdk.TokensFromConsensusPower(1e12, sdk.DefaultPowerReduction)), sdk.NewCoin(denoms.USDC, sdk.TokensFromConsensusPower(1e12, sdk.DefaultPowerReduction)), ), - PruningStrategy: types.PruningOptionNothing, - CleanupDir: true, - SigningAlgo: string(hd.Secp256k1Type), - KeyringOptions: []keyring.Option{}, + TimeoutCommit: time.Second / 2, + TxConfig: encCfg.TxConfig, } } -// New creates a new Network for integration tests. -func New(logger Logger, baseDir string, cfg Config) (*Network, error) { +/* +New creates a new Network for integration tests. + +Example: + + import ( + "suite" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" + ) + + var s *suite.Suite // For some test suite... + encodingConfig := app.MakeEncodingConfig() + genesisState := genesis.NewTestGenesisState(encodingConfig) + cfg = testnetwork.BuildNetworkConfig(genesisState) + network, err := testnetwork.New(s.T(), s.T().TempDir(), cfg) + s.Require().NoError(err) +*/ +func New(logger Logger, baseDir string, cfg Config) (network *Network, err error) { // only one caller/test can create and use a network at a time logger.Log("acquiring test network lock") lock.Lock() - network := &Network{ + // This is a `defer` pattern to add behavior that runs in the case that the error is + // non-nil, creating a concise way to add extra information. + defer func() { + if err != nil { + err = fmt.Errorf("error starting test network: %w", err) + } + }() + + network = &Network{ Logger: logger, BaseDir: baseDir, Validators: make([]*Validator, cfg.NumValidators), @@ -212,7 +180,7 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { buf := bufio.NewReader(os.Stdin) // generate private keys, node IDs, and initial transactions - for i := 0; i < cfg.NumValidators; i++ { + for valIdx := 0; valIdx < cfg.NumValidators; valIdx++ { appCfg := serverconfig.DefaultConfig() appCfg.Pruning = cfg.PruningStrategy appCfg.MinGasPrices = cfg.MinGasPrices @@ -222,7 +190,12 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { ctx := server.NewDefaultContext() tmCfg := ctx.Config + tmCfg.Consensus.TimeoutCommit = cfg.TimeoutCommit + if appconst.HavePebbleDBBuildTag { + defaultTmCfg := appconst.NewDefaultTendermintConfig() + tmCfg.DBBackend = defaultTmCfg.DBBackend + } // Only allow the first validator to expose an RPC, API and gRPC // server/client due to Tendermint in-process constraints. @@ -231,21 +204,21 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { appCfg.GRPC.Enable = false appCfg.GRPCWeb.Enable = false apiListenAddr := "" - if i == 0 { + if valIdx == 0 { if cfg.APIAddress != "" { apiListenAddr = cfg.APIAddress } else { var err error apiListenAddr, _, err = server.FreeTCPAddr() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get free TCP address for API: %w", err) } } appCfg.API.Address = apiListenAddr apiURL, err := url.Parse(apiListenAddr) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to parse API listen address: %w", err) } apiAddr = fmt.Sprintf("http://%s:%s", apiURL.Hostname(), apiURL.Port()) @@ -276,33 +249,44 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { } appCfg.GRPCWeb.Address = fmt.Sprintf("0.0.0.0:%s", grpcWebPort) appCfg.GRPCWeb.Enable = true + + if cfg.JSONRPCAddress != "" { + appCfg.JSONRPC.Address = cfg.JSONRPCAddress + } else { + _, jsonRPCPort, err := server.FreeTCPAddr() + if err != nil { + return nil, err + } + appCfg.JSONRPC.Address = fmt.Sprintf("0.0.0.0:%s", jsonRPCPort) + } + appCfg.JSONRPC.Enable = true + appCfg.JSONRPC.API = serverconfig.GetAPINamespaces() } - loggerNoOp := log.NewNopLogger() + serverCtxLogger := log.NewNopLogger() if cfg.EnableTMLogging { - loggerNoOp = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + serverCtxLogger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) } + ctx.Logger = serverCtxLogger - ctx.Logger = loggerNoOp - - nodeDirName := fmt.Sprintf("node%d", i) + nodeDirName := fmt.Sprintf("node%d", valIdx) nodeDir := filepath.Join(network.BaseDir, nodeDirName, "simd") clientDir := filepath.Join(network.BaseDir, nodeDirName, "simcli") gentxsDir := filepath.Join(network.BaseDir, "gentxs") err := os.MkdirAll(filepath.Join(nodeDir, "config"), 0o755) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create node configuration directory: %w", err) } err = os.MkdirAll(clientDir, 0o755) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create node client directory: %w", err) } tmCfg.SetRoot(nodeDir) tmCfg.Moniker = nodeDirName - monikers[i] = nodeDirName + monikers[valIdx] = nodeDirName proxyAddr, _, err := server.FreeTCPAddr() if err != nil { @@ -321,29 +305,31 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { nodeID, pubKey, err := genutil.InitializeNodeValidatorFiles(tmCfg) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize node validator files: %w", err) } - nodeIDs[i] = nodeID - valPubKeys[i] = pubKey + nodeIDs[valIdx] = nodeID + valPubKeys[valIdx] = pubKey kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, clientDir, buf, cfg.Codec, cfg.KeyringOptions...) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create new keyring: %w", err) } keyringAlgos, _ := kb.SupportedAlgorithms() algo, err := keyring.NewSigningAlgoFromString(cfg.SigningAlgo, keyringAlgos) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to parse signing algorithm: %w", err) } var mnemonic string - if i < len(cfg.Mnemonics) { - mnemonic = cfg.Mnemonics[i] + if valIdx < len(cfg.Mnemonics) { + mnemonic = cfg.Mnemonics[valIdx] } addr, secret, err := sdktestutil.GenerateSaveCoinKey(kb, nodeDirName, mnemonic, true, algo) + ethAddr := common.BytesToAddress(addr.Bytes()) + if err != nil { return nil, err } @@ -371,18 +357,18 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: balances.Sort()}) genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) - commission, err := sdk.NewDecFromStr("0.05") + commission, err := math.LegacyNewDecFromStr("0.05") if err != nil { return nil, err } createValMsg, err := stakingtypes.NewMsgCreateValidator( sdk.ValAddress(addr), - valPubKeys[i], + valPubKeys[valIdx], sdk.NewCoin(cfg.BondDenom, cfg.BondedTokens), stakingtypes.NewDescription(nodeDirName, "", "", "", ""), - stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), - sdk.OneInt(), + stakingtypes.NewCommissionRates(commission, math.LegacyOneDec(), math.LegacyOneDec()), + math.OneInt(), ) if err != nil { return nil, err @@ -393,8 +379,8 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { return nil, err } - memo := fmt.Sprintf("%s@%s:%s", nodeIDs[i], p2pURL.Hostname(), p2pURL.Port()) - fee := sdk.NewCoins(sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), sdk.ZeroInt())) + memo := fmt.Sprintf("%s@%s:%s", nodeIDs[valIdx], p2pURL.Hostname(), p2pURL.Port()) + fee := sdk.NewCoins(sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), math.ZeroInt())) txBuilder := cfg.TxConfig.NewTxBuilder() err = txBuilder.SetMsgs(createValMsg) if err != nil { @@ -425,7 +411,7 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { return nil, err } - serverconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), appCfg) + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), appCfg) clientCtx := client.Context{}. WithKeyringDir(clientDir). @@ -438,11 +424,12 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { WithTxConfig(cfg.TxConfig). WithAccountRetriever(cfg.AccountRetriever) - network.Validators[i] = &Validator{ + network.Validators[valIdx] = &Validator{ AppConfig: appCfg, ClientCtx: clientCtx, Ctx: ctx, Dir: filepath.Join(network.BaseDir, nodeDirName), + Logger: logger, NodeID: nodeID, PubKey: pubKey, Moniker: nodeDirName, @@ -450,12 +437,13 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { P2PAddress: tmCfg.P2P.ListenAddress, APIAddress: apiAddr, Address: addr, + EthAddress: ethAddr, ValAddress: sdk.ValAddress(addr), secretMnemonic: secret, } } - err := initGenFiles(cfg, genAccounts, genBalances, genFiles) + err = initGenFiles(cfg, genAccounts, genBalances, genFiles) if err != nil { return nil, err } @@ -466,9 +454,9 @@ func New(logger Logger, baseDir string, cfg Config) (*Network, error) { logger.Log("starting test network...") for idx, v := range network.Validators { - err := startInProcess(cfg, v) + err := startNodeAndServers(cfg, v) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to start node: %w", err) } logger.Log("started validator", idx) } @@ -506,7 +494,7 @@ func (n *Network) LatestHeight() (int64, error) { // committed after a given block. If that height is not reached within a timeout, // an error is returned. Regardless, the latest height queried is returned. func (n *Network) WaitForHeight(h int64) (int64, error) { - return n.WaitForHeightWithTimeout(h, 40*time.Second) + return n.WaitForHeightWithTimeout(h, 5*time.Minute) } // WaitForHeightWithTimeout is the same as WaitForHeight except the caller can @@ -543,17 +531,23 @@ func (n *Network) WaitForHeightWithTimeout(h int64, t time.Duration) (int64, err // WaitForNextBlock waits for the next block to be committed, returning an error // upon failure. -func (n *Network) WaitForNextBlock() error { +func (n *Network) WaitForNextBlockVerbose() (int64, error) { lastBlock, err := n.LatestHeight() if err != nil { - return err + return -1, err } - _, err = n.WaitForHeight(lastBlock + 1) + newBlock := lastBlock + 1 + _, err = n.WaitForHeight(newBlock) if err != nil { - return err + return lastBlock, err } + return newBlock, err +} + +func (n *Network) WaitForNextBlock() error { + _, err := n.WaitForNextBlockVerbose() return err } @@ -598,84 +592,47 @@ func (n *Network) Cleanup() { n.Logger.Log("cleaning up test network...") - for _, v := range n.Validators { - if v.tmNode != nil && v.tmNode.IsRunning() { - _ = v.tmNode.Stop() - } - - if v.api != nil { - _ = v.api.Close() - } - - if v.grpc != nil { - v.grpc.Stop() - if v.grpcWeb != nil { - _ = v.grpcWeb.Close() - } - } - } + // We use a wait group here to ensure that all services are stopped before + // cleaning up. + var waitGroup sync.WaitGroup - // Give a brief pause for things to finish closing in other processes. - // Hopefully this helps with the address-in-use errors. 100ms chosen - // randomly. - time.Sleep(100 * time.Millisecond) + for _, v := range n.Validators { + waitGroup.Add(1) - if n.Config.CleanupDir { - _ = os.RemoveAll(n.BaseDir) + go func(v *Validator) { + defer waitGroup.Done() + stopValidatorNode(v) + }(v) } - n.Logger.Log("finished cleaning up test network") -} - -func (val Validator) SecretMnemonic() string { - return val.secretMnemonic -} - -func (val Validator) SecretMnemonicSlice() []string { - return strings.Fields(val.secretMnemonic) -} - -// LogMnemonic logs a secret to the network's logger for debugging and manual -// testing -func LogMnemonic(l Logger, secret string) { - lines := []string{ - "THIS MNEMONIC IS FOR TESTING PURPOSES ONLY", - "DO NOT USE IN PRODUCTION", - "", - strings.Join(strings.Fields(secret)[0:8], " "), - strings.Join(strings.Fields(secret)[8:16], " "), - strings.Join(strings.Fields(secret)[16:24], " "), - } + waitGroup.Wait() - lineLengths := make([]int, len(lines)) - for i, line := range lines { - lineLengths[i] = len(line) - } + // TODO: Is there a cleaner way to do this with a guarnteed synchronous on + // each "Validator.tmNode"? + // https://github.com/NibiruChain/nibiru/issues/1955 - maxLineLength := 0 - for _, lineLen := range lineLengths { - if lineLen > maxLineLength { - maxLineLength = lineLen + // Give a brief pause for things to finish closing in other processes. + // Hopefully this helps with the address-in-use errors. + // Timeout of 100ms chosen randomly. + // Timeout of 250ms chosen because 100ms was not enough. | 2024-07-02 + maxRetries := 20 + stopped := false + for i := 0; i < maxRetries; i++ { + if ValidatorsStopped(n.Validators) { + stopped = true + break } + time.Sleep(500 * time.Millisecond) } - - l.Log("\n") - l.Log(strings.Repeat("+", maxLineLength+8)) - for _, line := range lines { - l.Logf("++ %s ++\n", centerText(line, maxLineLength)) + if !stopped { + panic("cleanup did not succeed within the max retry count") } - l.Log(strings.Repeat("+", maxLineLength+8)) - l.Log("\n") -} -// centerText: Centers text across a fixed width, filling either side with -// whitespace buffers -func centerText(text string, width int) string { - textLen := len(text) - leftBuffer := strings.Repeat(" ", (width-textLen)/2) - rightBuffer := strings.Repeat(" ", (width-textLen)/2+(width-textLen)%2) + if n.Config.CleanupDir { + _ = os.RemoveAll(n.BaseDir) + } - return fmt.Sprintf("%s%s%s", leftBuffer, text, rightBuffer) + n.Logger.Log("finished cleaning up test network") } func (n *Network) keyBaseAndInfoForAddr(addr sdk.AccAddress) (keyring.Keyring, *keyring.Record, error) { diff --git a/x/common/testutil/cli/network_config.go b/x/common/testutil/testnetwork/network_config.go similarity index 91% rename from x/common/testutil/cli/network_config.go rename to x/common/testutil/testnetwork/network_config.go index 91fc799c6..bf46c7cc0 100644 --- a/x/common/testutil/cli/network_config.go +++ b/x/common/testutil/testnetwork/network_config.go @@ -1,21 +1,21 @@ -package cli +package testnetwork import ( "encoding/json" "time" sdkmath "cosmossdk.io/math" - tmconfig "github.com/cometbft/cometbft/config" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keyring" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" sdk "github.com/cosmos/cosmos-sdk/types" + + serverconfig "github.com/NibiruChain/nibiru/v2/app/server/config" ) -// Config: Defines the parameters needed to start a local test network. +// Config: Defines the parameters needed to start a local test [Network]. type Config struct { Codec codec.Codec LegacyAmino *codec.LegacyAmino // TODO: Remove! @@ -44,6 +44,7 @@ type Config struct { RPCAddress string // RPC listen address (including port) APIAddress string // REST API listen address (including port) GRPCAddress string // GRPC server listen address (including port) + JSONRPCAddress string // JSON-RPC listen address (including port) } func (cfg *Config) AbsorbServerConfig(srvCfg *serverconfig.Config) { diff --git a/x/common/testutil/cli/network_test.go b/x/common/testutil/testnetwork/network_test.go similarity index 58% rename from x/common/testutil/cli/network_test.go rename to x/common/testutil/testnetwork/network_test.go index d9ace8ad2..a36f2f658 100644 --- a/x/common/testutil/cli/network_test.go +++ b/x/common/testutil/testnetwork/network_test.go @@ -1,5 +1,5 @@ // Alteration of [network/network_test.go](https://github.com/cosmos/cosmos-sdk/blob/v0.45.15/testutil/network/network_test.go) -package cli_test +package testnetwork_test import ( "fmt" @@ -11,41 +11,39 @@ import ( sdktestutil "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/app/codec" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/codec" "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/x/common/testutil/cli" - "github.com/NibiruChain/nibiru/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" ) func TestIntegrationTestSuite_RunAll(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) + testutil.RetrySuiteRunIfDbClosed(t, func() { + suite.Run(t, new(TestSuite)) + }, 2) } -type IntegrationTestSuite struct { +// Assert network cleanup +var _ suite.TearDownAllSuite = (*TestSuite)(nil) + +type TestSuite struct { suite.Suite - network *cli.Network - cfg *cli.Config + network *testnetwork.Network + cfg *testnetwork.Config } -func (s *IntegrationTestSuite) SetupSuite() { - /* Make test skip if -short is not used: - All tests: `go test ./...` - Unit tests only: `go test ./... -short` - Integration tests only: `go test ./... -run Integration` - https://stackoverflow.com/a/41407042/13305627 */ - if testing.Short() { - s.T().Skip("skipping integration test suite") - } - s.T().Log("setting up integration test suite") +func (s *TestSuite) SetupSuite() { + testutil.BeforeIntegrationSuite(s.T()) encConfig := app.MakeEncodingConfig() - cfg := new(cli.Config) - *cfg = cli.BuildNetworkConfig(genesis.NewTestGenesisState(encConfig)) - network, err := cli.New( + cfg := new(testnetwork.Config) + *cfg = testnetwork.BuildNetworkConfig(genesis.NewTestGenesisState(encConfig)) + network, err := testnetwork.New( s.T(), s.T().TempDir(), *cfg, @@ -60,12 +58,12 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Require().NoError(err) } -func (s *IntegrationTestSuite) TearDownSuite() { +func (s *TestSuite) TearDownSuite() { s.T().Log("tearing down integration test suite") s.network.Cleanup() } -func (s *IntegrationTestSuite) TestNetwork_Liveness() { +func (s *TestSuite) TestNetwork_Liveness() { height, err := s.network.WaitForHeightWithTimeout(4, time.Minute) s.Require().NoError(err, "expected to reach 4 blocks; got %d", height) @@ -73,20 +71,20 @@ func (s *IntegrationTestSuite) TestNetwork_Liveness() { s.NoError(err) } -func (s *IntegrationTestSuite) TestNetwork_LatestHeight() { +func (s *TestSuite) TestNetwork_LatestHeight() { height, err := s.network.LatestHeight() s.NoError(err) s.Positive(height) - sadNetwork := new(cli.Network) + sadNetwork := new(testnetwork.Network) _, err = sadNetwork.LatestHeight() s.Error(err) } -func (s *IntegrationTestSuite) TestLogMnemonic() { - kring, algo, nodeDirName := cli.NewKeyring(s.T()) +func (s *TestSuite) TestLogMnemonic() { + kring, algo, nodeDirName := testnetwork.NewKeyring(s.T()) - var cdc sdkcodec.Codec = codec.MakeEncodingConfig().Marshaler + var cdc sdkcodec.Codec = codec.MakeEncodingConfig().Codec _, mnemonic, err := sdktestutil.GenerateCoinKey(algo, cdc) s.NoError(err) @@ -96,18 +94,18 @@ func (s *IntegrationTestSuite) TestLogMnemonic() { ) s.NoError(err) - cli.LogMnemonic(&mockLogger{ + testnetwork.LogMnemonic(&mockLogger{ Logs: []string{}, }, secret) } -func (s *IntegrationTestSuite) TestValidatorGetSecret() { +func (s *TestSuite) TestValidatorGetSecret() { val := s.network.Validators[0] secret := val.SecretMnemonic() secretSlice := val.SecretMnemonicSlice() s.Equal(secret, strings.Join(secretSlice, " ")) - kring, algo, nodeDirName := cli.NewKeyring(s.T()) + kring, algo, nodeDirName := testnetwork.NewKeyring(s.T()) mnemonic := secret overwrite := true addrGenerated, secretGenerated, err := sdktestutil.GenerateSaveCoinKey( @@ -118,7 +116,7 @@ func (s *IntegrationTestSuite) TestValidatorGetSecret() { s.Equal(val.Address, addrGenerated) } -var _ cli.Logger = (*mockLogger)(nil) +var _ testnetwork.Logger = (*mockLogger)(nil) type mockLogger struct { Logs []string @@ -132,9 +130,9 @@ func (ml *mockLogger) Logf(format string, args ...interface{}) { ml.Logs = append(ml.Logs, fmt.Sprintf(format, args...)) } -func (s *IntegrationTestSuite) TestNewAccount() { +func (s *TestSuite) TestNewAccount() { s.NotPanics(func() { - addr := cli.NewAccount(s.network, "newacc") + addr := testnetwork.NewAccount(s.network, "newacc") s.NoError(sdk.VerifyAddressFormat(addr)) }) } diff --git a/x/common/testutil/cli/query.go b/x/common/testutil/testnetwork/query.go similarity index 91% rename from x/common/testutil/cli/query.go rename to x/common/testutil/testnetwork/query.go index 871cc525a..ff5ff189f 100644 --- a/x/common/testutil/cli/query.go +++ b/x/common/testutil/testnetwork/query.go @@ -1,4 +1,4 @@ -package cli +package testnetwork import ( "fmt" @@ -13,11 +13,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/common/asset" - oraclecli "github.com/NibiruChain/nibiru/x/oracle/client/cli" - oracletypes "github.com/NibiruChain/nibiru/x/oracle/types" - sudocli "github.com/NibiruChain/nibiru/x/sudo/cli" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + oraclecli "github.com/NibiruChain/nibiru/v2/x/oracle/cli" + oracletypes "github.com/NibiruChain/nibiru/v2/x/oracle/types" + sudocli "github.com/NibiruChain/nibiru/v2/x/sudo/cli" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) // ExecQueryOption defines a type which customizes a CLI query operation. diff --git a/x/common/testutil/testnetwork/start_node.go b/x/common/testutil/testnetwork/start_node.go new file mode 100644 index 000000000..93c7381ac --- /dev/null +++ b/x/common/testutil/testnetwork/start_node.go @@ -0,0 +1,175 @@ +package testnetwork + +import ( + "fmt" + "os" + "time" + + "cosmossdk.io/errors" + db "github.com/cometbft/cometbft-db" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/NibiruChain/nibiru/v2/app/server" + ethrpc "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + "github.com/NibiruChain/nibiru/v2/eth/rpc/rpcapi" + + "github.com/cosmos/cosmos-sdk/server/api" + servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" + srvtypes "github.com/cosmos/cosmos-sdk/server/types" + + "github.com/cometbft/cometbft/libs/log" + "github.com/cometbft/cometbft/node" + "github.com/cometbft/cometbft/p2p" + pvm "github.com/cometbft/cometbft/privval" + "github.com/cometbft/cometbft/proxy" + "github.com/cometbft/cometbft/rpc/client/local" +) + +func startNodeAndServers(cfg Config, val *Validator) error { + logger := val.Ctx.Logger + evmServerCtxLogger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + tmCfg := val.Ctx.Config + tmCfg.Instrumentation.Prometheus = false + + if err := val.AppConfig.ValidateBasic(); err != nil { + return err + } + + nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) + if err != nil { + return err + } + + app := cfg.AppConstructor(*val) + + genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) + tmNode, err := node.NewNode( + tmCfg, + pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), + nodeKey, + proxy.NewLocalClientCreator(app), + genDocProvider, + node.DefaultDBProvider, + node.DefaultMetricsProvider(tmCfg.Instrumentation), + logger.With("module", val.Moniker), + ) + if err != nil { + return fmt.Errorf("failed to construct Node: %w", err) + } + + if err := tmNode.Start(); err != nil { + return fmt.Errorf("failed Node.Start(): %w", err) + } + + val.tmNode = tmNode + val.tmNode.Logger = logger + + if val.RPCAddress != "" { + val.RPCClient = local.New(tmNode) + } + + // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. + if val.APIAddress != "" || val.AppConfig.GRPC.Enable { + val.ClientCtx = val.ClientCtx. + WithClient(val.RPCClient) + + // Add the tx service in the gRPC router. + app.RegisterTxService(val.ClientCtx) + + // Add the tendermint queries service in the gRPC router. + app.RegisterTendermintService(val.ClientCtx) + + val.EthRpc_NET = rpcapi.NewImplNetAPI(val.ClientCtx) + } + + if val.APIAddress != "" { + apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) + app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) + + errCh := make(chan error) + + go func() { + if err := apiSrv.Start(val.AppConfig.Config); err != nil { + errCh <- err + } + }() + + select { + case err := <-errCh: + return err + case <-time.After(srvtypes.ServerStartTime): // assume server started successfully + } + + val.api = apiSrv + } + + if val.AppConfig.GRPC.Enable { + grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC) + if err != nil { + return err + } + + val.grpc = grpcSrv + + if val.AppConfig.GRPCWeb.Enable { + val.grpcWeb, err = servergrpc.StartGRPCWeb(grpcSrv, val.AppConfig.Config) + if err != nil { + return err + } + } + } + + val.Ctx.Logger = evmServerCtxLogger + + useEthJsonRPC := val.AppConfig.JSONRPC.Enable && val.AppConfig.JSONRPC.Address != "" + if useEthJsonRPC { + if val.Ctx == nil || val.Ctx.Viper == nil { + return fmt.Errorf("validator %s context is nil", val.Moniker) + } + + tmEndpoint := "/websocket" + tmRPCAddr := fmt.Sprintf("tcp://%s", val.AppConfig.GRPC.Address) + + val.Logger.Log("Set EVM indexer") + + evmTxIndexer, evmTxIndexerService, err := server.OpenEVMIndexer(val.Ctx, db.NewMemDB(), val.ClientCtx) + if err != nil { + { + return fmt.Errorf("failed starting evm indexer service: %w", err) + } + } + val.EthTxIndexer = evmTxIndexer + val.EthTxIndexerService = evmTxIndexerService + + val.jsonrpc, val.jsonrpcDone, err = server.StartJSONRPC(val.Ctx, val.ClientCtx, tmRPCAddr, tmEndpoint, val.AppConfig, val.EthTxIndexer) + if err != nil { + return errors.Wrap(err, "failed to start JSON-RPC server") + } + + address := fmt.Sprintf("http://%s", val.AppConfig.JSONRPC.Address) + + val.JSONRPCClient, err = ethclient.Dial(address) + if err != nil { + return fmt.Errorf("failed to dial JSON-RPC at address %s: %w", val.AppConfig.JSONRPC.Address, err) + } + + val.Logger.Log("Set up Ethereum JSON-RPC client objects") + val.EthRpcQueryClient = ethrpc.NewQueryClient(val.ClientCtx) + val.EthRpcBackend = backend.NewBackend( + val.Ctx, + val.Ctx.Logger, + val.ClientCtx, + val.AppConfig.JSONRPC.AllowUnprotectedTxs, + val.EthTxIndexer, + ) + + val.Logger.Log("Expose typed methods for each namespace") + val.EthRPC_ETH = rpcapi.NewImplEthAPI(val.Ctx.Logger, val.EthRpcBackend) + val.EthRpc_WEB3 = rpcapi.NewImplWeb3API() + + val.Ctx.Logger = logger // set back to normal setting + } + + return nil +} diff --git a/x/common/testutil/cli/tx.go b/x/common/testutil/testnetwork/tx.go similarity index 89% rename from x/common/testutil/cli/tx.go rename to x/common/testutil/testnetwork/tx.go index b7e43b036..279381a2a 100644 --- a/x/common/testutil/cli/tx.go +++ b/x/common/testutil/testnetwork/tx.go @@ -1,9 +1,10 @@ -package cli +package testnetwork import ( "context" "fmt" + "cosmossdk.io/math" "github.com/cometbft/cometbft/abci/types" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" @@ -12,8 +13,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/common" - "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" ) type ExecTxOption func(*execTxOptions) @@ -60,7 +61,7 @@ type execTxOptions struct { } var DEFAULT_TX_OPTIONS = execTxOptions{ - Fees: sdk.NewCoins(sdk.NewCoin(denoms.NIBI, sdk.NewInt(1000))), + Fees: sdk.NewCoins(sdk.NewCoin(denoms.NIBI, math.NewInt(1000))), Gas: 2000000, SkipConfirmation: true, BroadcastMode: flags.BroadcastSync, @@ -125,7 +126,9 @@ func (network *Network) ExecTxCmd( } func (chain *Network) BroadcastMsgs( - from sdk.AccAddress, msgs ...sdk.Msg, + from sdk.AccAddress, + accountSequence *uint64, + msgs ...sdk.Msg, ) (*sdk.TxResponse, error) { cfg := chain.Config kb, info, err := chain.keyBaseAndInfoForAddr(from) @@ -140,14 +143,19 @@ func (chain *Network) BroadcastMsgs( return nil, err } - txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(cfg.BondDenom, sdk.NewInt(1000)))) - txBuilder.SetGasLimit(uint64(1 * common.TO_MICRO)) + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(cfg.BondDenom, math.NewInt(1000)))) + txBuilder.SetGasLimit(uint64(10 * common.TO_MICRO)) acc, err := cfg.AccountRetriever.GetAccount(chain.Validators[0].ClientCtx, from) if err != nil { return nil, err } - + var sequence uint64 + if accountSequence != nil { + sequence = *accountSequence + } else { + sequence = acc.GetSequence() + } txFactory := tx.Factory{} txFactory = txFactory. WithChainID(cfg.ChainID). @@ -155,7 +163,7 @@ func (chain *Network) BroadcastMsgs( WithTxConfig(cfg.TxConfig). WithAccountRetriever(cfg.AccountRetriever). WithAccountNumber(acc.GetAccountNumber()). - WithSequence(acc.GetSequence()) + WithSequence(sequence) err = tx.Sign(txFactory, info.Name, txBuilder, true) if err != nil { diff --git a/x/common/testutil/cli/tx_test.go b/x/common/testutil/testnetwork/tx_test.go similarity index 61% rename from x/common/testutil/cli/tx_test.go rename to x/common/testutil/testnetwork/tx_test.go index 85aedd0b8..267140460 100644 --- a/x/common/testutil/cli/tx_test.go +++ b/x/common/testutil/testnetwork/tx_test.go @@ -1,23 +1,22 @@ -package cli_test +package testnetwork_test import ( - "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/cli" - + "cosmossdk.io/math" bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/cli" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" ) -func (s *IntegrationTestSuite) TestSendTx() { +func (s *TestSuite) TestSendTx() { fromAddr := s.network.Validators[0].Address toAddr := testutil.AccAddress() - sendCoin := sdk.NewCoin(denoms.NIBI, sdk.NewInt(42)) - txResp, err := s.network.BroadcastMsgs(fromAddr, &banktypes.MsgSend{ + sendCoin := sdk.NewCoin(denoms.NIBI, math.NewInt(42)) + txResp, err := s.network.BroadcastMsgs(fromAddr, nil, &banktypes.MsgSend{ FromAddress: fromAddr.String(), ToAddress: toAddr.String(), Amount: sdk.NewCoins(sendCoin), @@ -27,18 +26,18 @@ func (s *IntegrationTestSuite) TestSendTx() { s.EqualValues(0, txResp.Code) } -func (s *IntegrationTestSuite) TestExecTx() { +func (s *TestSuite) TestExecTx() { fromAddr := s.network.Validators[0].Address toAddr := testutil.AccAddress() - sendCoin := sdk.NewCoin(denoms.NIBI, sdk.NewInt(69)) + sendCoin := sdk.NewCoin(denoms.NIBI, math.NewInt(69)) args := []string{fromAddr.String(), toAddr.String(), sendCoin.String()} txResp, err := s.network.ExecTxCmd(bankcli.NewSendTxCmd(), fromAddr, args) s.NoError(err) s.EqualValues(0, txResp.Code) - s.T().Run("test tx option changes", func(t *testing.T) { - defaultOpts := cli.DEFAULT_TX_OPTIONS - opts := cli.WithTxOptions(cli.TxOptionChanges{ + s.Run("test tx option changes", func() { + defaultOpts := testnetwork.DEFAULT_TX_OPTIONS + opts := testnetwork.WithTxOptions(testnetwork.TxOptionChanges{ BroadcastMode: &defaultOpts.BroadcastMode, CanFail: &defaultOpts.CanFail, Fees: &defaultOpts.Fees, @@ -51,24 +50,25 @@ func (s *IntegrationTestSuite) TestExecTx() { s.EqualValues(0, txResp.Code) }) - s.T().Run("fail when validators are missing", func(t *testing.T) { - networkNoVals := new(cli.Network) + s.Run("fail when validators are missing", func() { + networkNoVals := new(testnetwork.Network) *networkNoVals = *s.network - networkNoVals.Validators = []*cli.Validator{} + networkNoVals.Validators = []*testnetwork.Validator{} _, err := networkNoVals.ExecTxCmd(bankcli.NewTxCmd(), fromAddr, args) s.Error(err) s.Contains(err.Error(), "") }) } -func (s *IntegrationTestSuite) TestFillWalletFromValidator() { +func (s *TestSuite) TestFillWalletFromValidator() { toAddr := testutil.AccAddress() val := s.network.Validators[0] funds := sdk.NewCoins( sdk.NewInt64Coin(denoms.NIBI, 420), ) feeDenom := denoms.NIBI - s.NoError(cli.FillWalletFromValidator( + _, err := testnetwork.FillWalletFromValidator( toAddr, funds, val, feeDenom, - )) + ) + s.Require().NoError(err) } diff --git a/x/common/testutil/cli/util.go b/x/common/testutil/testnetwork/util.go similarity index 65% rename from x/common/testutil/cli/util.go rename to x/common/testutil/testnetwork/util.go index 9e60c46ec..6ecb36bba 100644 --- a/x/common/testutil/cli/util.go +++ b/x/common/testutil/testnetwork/util.go @@ -1,4 +1,4 @@ -package cli +package testnetwork import ( "encoding/json" @@ -6,29 +6,22 @@ import ( "os" "path/filepath" "testing" - "time" + + "github.com/ethereum/go-ethereum/common" tmtypes "github.com/cometbft/cometbft/abci/types" sdkcodec "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/NibiruChain/nibiru/app/codec" + "github.com/NibiruChain/nibiru/v2/app/codec" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/server/api" - servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" - srvtypes "github.com/cosmos/cosmos-sdk/server/types" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" tmos "github.com/cometbft/cometbft/libs/os" - "github.com/cometbft/cometbft/node" - "github.com/cometbft/cometbft/p2p" - pvm "github.com/cometbft/cometbft/privval" - "github.com/cometbft/cometbft/proxy" - "github.com/cometbft/cometbft/rpc/client/local" "github.com/cometbft/cometbft/types" tmtime "github.com/cometbft/cometbft/types/time" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -36,99 +29,6 @@ import ( genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" ) -func startInProcess(cfg Config, val *Validator) error { - logger := val.Ctx.Logger - tmCfg := val.Ctx.Config - tmCfg.Instrumentation.Prometheus = false - - if err := val.AppConfig.ValidateBasic(); err != nil { - return err - } - - nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) - if err != nil { - return err - } - - app := cfg.AppConstructor(*val) - - genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) - tmNode, err := node.NewNode( - tmCfg, - pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), - nodeKey, - proxy.NewLocalClientCreator(app), - genDocProvider, - node.DefaultDBProvider, - node.DefaultMetricsProvider(tmCfg.Instrumentation), - logger.With("module", val.Moniker), - ) - if err != nil { - return err - } - - if err := tmNode.Start(); err != nil { - return err - } - - val.tmNode = tmNode - - if val.RPCAddress != "" { - val.RPCClient = local.New(tmNode) - } - - // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. - if val.APIAddress != "" || val.AppConfig.GRPC.Enable { - val.ClientCtx = val.ClientCtx. - WithClient(val.RPCClient) - - // Add the tx service in the gRPC router. - app.RegisterTxService(val.ClientCtx) - - // Add the tendermint queries service in the gRPC router. - app.RegisterTendermintService(val.ClientCtx) - } - - if val.APIAddress != "" { - apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) - app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) - - errCh := make(chan error) - - go func() { - if err := apiSrv.Start(*val.AppConfig); err != nil { - errCh <- err - } - }() - - select { - case err := <-errCh: - return err - case <-time.After(srvtypes.ServerStartTime): // assume server started successfully - } - - val.api = apiSrv - } - - if val.AppConfig.GRPC.Enable { - grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC) - if err != nil { - return err - } - - val.grpc = grpcSrv - - if val.AppConfig.GRPCWeb.Enable { - val.grpcWeb, err = servergrpc.StartGRPCWeb(grpcSrv, *val.AppConfig) - if err != nil { - return err - } - } - } - - return nil -} - func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { genTime := tmtime.Now() @@ -226,7 +126,7 @@ func writeFile(name string, dir string, contents []byte) error { // validator. func FillWalletFromValidator( addr sdk.AccAddress, balance sdk.Coins, val *Validator, feesDenom string, -) error { +) (*sdk.TxResponse, error) { rawResp, err := clitestutil.MsgSendExec( val.ClientCtx, val.Address, @@ -237,19 +137,19 @@ func FillWalletFromValidator( fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewInt64Coin(feesDenom, 10000)), ) if err != nil { - return err + return nil, err } return txOK(val.ClientCtx.Codec, rawResp.Bytes()) } -func txOK(jsonCodec sdkcodec.JSONCodec, txBytes []byte) error { +func txOK(jsonCodec sdkcodec.JSONCodec, txBytes []byte) (*sdk.TxResponse, error) { resp := new(sdk.TxResponse) jsonCodec.MustUnmarshalJSON(txBytes, resp) if resp.Code != tmtypes.CodeTypeOK { - return fmt.Errorf("%s", resp.RawLog) + return resp, fmt.Errorf("%s", resp.RawLog) } - return nil + return resp, nil } /* @@ -284,12 +184,17 @@ func NewAccount(network *Network, uid string) sdk.AccAddress { return addr } +func NewEthAccount(network *Network, uid string) common.Address { + addr := NewAccount(network, uid) + return common.BytesToAddress(addr.Bytes()) +} + func NewKeyring(t *testing.T) ( kring keyring.Keyring, algo keyring.SignatureAlgo, nodeDirName string, ) { - var cdc sdkcodec.Codec = codec.MakeEncodingConfig().Marshaler + var cdc sdkcodec.Codec = codec.MakeEncodingConfig().Codec kring = keyring.NewInMemory(cdc) nodeDirName = t.TempDir() algo = hd.Secp256k1 diff --git a/x/common/testutil/testnetwork/validator_node.go b/x/common/testutil/testnetwork/validator_node.go new file mode 100644 index 000000000..f4dc3f016 --- /dev/null +++ b/x/common/testutil/testnetwork/validator_node.go @@ -0,0 +1,292 @@ +package testnetwork + +import ( + "context" + "fmt" + "math/big" + "net/http" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/suite" + + appserver "github.com/NibiruChain/nibiru/v2/app/server" + + serverconfig "github.com/NibiruChain/nibiru/v2/app/server/config" + "github.com/NibiruChain/nibiru/v2/eth" + ethrpc "github.com/NibiruChain/nibiru/v2/eth/rpc" + "github.com/NibiruChain/nibiru/v2/eth/rpc/backend" + "github.com/NibiruChain/nibiru/v2/eth/rpc/rpcapi" + + "github.com/cometbft/cometbft/node" + tmclient "github.com/cometbft/cometbft/rpc/client" + cmtcore "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cosmos/cosmos-sdk/client" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server" + serverapi "github.com/cosmos/cosmos-sdk/server/api" + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc" + + geth "github.com/ethereum/go-ethereum" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" +) + +// Validator defines an in-process Tendermint validator node. Through this +// object, a client can make RPC and API calls and interact with any client +// command or handler. +type Validator struct { + AppConfig *serverconfig.Config + ClientCtx client.Context + Ctx *server.Context + // Dir is the root directory of the validator node data and config. Passed to the Tendermint config. + Dir string + + // NodeID is a unique ID for the validator generated when the + // 'cli.Network' is started. + NodeID string + PubKey cryptotypes.PubKey + + // Moniker is a human-readable name that identifies a validator. A + // moniker is optional and may be empty. + Moniker string + + // APIAddress is the endpoint that the validator API server binds to. + // Only the first validator of a 'cli.Network' exposes the full API. + APIAddress string + + // RPCAddress is the endpoint that the RPC server binds to. Only the + // first validator of a 'cli.Network' exposes the full API. + RPCAddress string + + // P2PAddress is the endpoint that the RPC server binds to. The P2P + // server handles Tendermint peer-to-peer (P2P) networking and is + // critical for blockchain replication and consensus. It allows nodes + // to gossip blocks, transactions, and consensus messages. Only the + // first validator of a 'cli.Network' exposes the full API. + P2PAddress string + + // Address - account address + Address sdk.AccAddress + + // EthAddress - Ethereum address + EthAddress common.Address + + // ValAddress - validator operator (valoper) address + ValAddress sdk.ValAddress + + // RPCClient wraps most important rpc calls a client would make to + // listen for events, test if it also implements events.EventSwitch. + // + // RPCClient implementations in "github.com/cometbft/cometbft/rpc" v0.37.2: + // - rpc.HTTP + // - rpc.Local + RPCClient tmclient.Client + + JSONRPCClient *ethclient.Client + EthRpcQueryClient *ethrpc.QueryClient + EthRpcBackend *backend.Backend + EthTxIndexer eth.EVMTxIndexer + EthTxIndexerService *appserver.EVMTxIndexerService + + EthRPC_ETH *rpcapi.EthAPI + EthRpc_WEB3 *rpcapi.APIWeb3 + EthRpc_NET *rpcapi.NetAPI + + Logger Logger + + tmNode *node.Node + + // API exposes the app's REST and gRPC interfaces, allowing clients to + // read from state and broadcast txs. The API server connects to the + // underlying ABCI application. + api *serverapi.Server + grpc *grpc.Server + grpcWeb *http.Server + secretMnemonic string + jsonrpc *http.Server + jsonrpcDone chan struct{} +} + +// stopValidatorNode shuts down all services associated with a validator node. +// +// It gracefully stops the Tendermint node, API, gRPC, gRPC-Web, and JSON-RPC +// services. This function is designed to be run concurrently for multiple +// validators during network cleanup. +// +// The function uses graceful shutdown methods where available to allow ongoing +// operations to complete before terminating. This approach helps prevent +// resource leaks and ensures a clean shutdown of all components. +// +// Parameters: +// - v: Pointer to the Validator struct containing service references. +// +// Note: Errors during shutdown are currently ignored to ensure all services +// attempt to stop, even if one fails. Consider adding error logging for +// debugging in production environments. +func stopValidatorNode(v *Validator) { + if v.tmNode != nil && v.tmNode.IsRunning() { + if err := v.tmNode.Stop(); err != nil { + v.Logger.Logf("Error stopping Validator.tmNode: %w", err) + } + v.tmNode.Wait() // Wait for the service to fully stop + } + + if v.api != nil { + // Close the API server. + // Any blocked "Accept" operations will be unblocked and return errors. + err := v.api.Close() + if err != nil { + v.Logger.Logf("❌ Error closing the API server: %w", err) + } + } + + if v.grpc != nil { + // GracefulStop stops the gRPC server gracefully. It stops the server from + // accepting new connections and RPCs and blocks until all the pending RPCs are + // finished. + v.grpc.GracefulStop() + } + + if v.grpcWeb != nil { + err := v.grpcWeb.Close() + if err != nil { + v.Logger.Logf("❌ Error closing the gRPC web server: %w", err) + } + } + + if v.jsonrpc != nil { + // Note that this is a graceful shutdown replacement for: + // _ = v.jsonrpc.Close() + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + if err := v.jsonrpc.Shutdown(ctx); err != nil { + // Log the error or handle it as appropriate for your application + v.Logger.Logf("❌ Error shutting down JSON-RPC server: %w", err) + } else { + v.Logger.Log("✅ Successfully shut down JSON-RPC server") + v.jsonrpc = nil + } + if v.EthTxIndexerService != nil { + err := v.EthTxIndexerService.Stop() + if err != nil { + v.Logger.Logf("❌ Error shutting down EVMTxIndexerService: %w", err) + } else { + v.Logger.Log("✅ Successfully shut down EVMTxIndexerService") + } + } + } + + if v.tmNode != nil { + v.tmNode.Wait() + } +} + +func ValidatorsStopped(vals []*Validator) (stopped bool) { + for _, v := range vals { + if !v.IsStopped() { + return false + } + } + return true +} + +// IsStopped returns true if the validator node is stopped +func (v *Validator) IsStopped() bool { + switch { + case v == nil: + return true + case v.tmNode == nil: + return true + case v.tmNode.IsRunning(): + return false + } + return true +} + +func (val Validator) SecretMnemonic() string { + return val.secretMnemonic +} + +func (val Validator) SecretMnemonicSlice() []string { + return strings.Fields(val.secretMnemonic) +} + +// LogMnemonic logs a secret to the network's logger for debugging and manual +// testing +func LogMnemonic(l Logger, secret string) { + lines := []string{ + "THIS MNEMONIC IS FOR TESTING PURPOSES ONLY", + "DO NOT USE IN PRODUCTION", + "", + strings.Join(strings.Fields(secret)[0:8], " "), + strings.Join(strings.Fields(secret)[8:16], " "), + strings.Join(strings.Fields(secret)[16:24], " "), + } + + lineLengths := make([]int, len(lines)) + for i, line := range lines { + lineLengths[i] = len(line) + } + + maxLineLength := 0 + for _, lineLen := range lineLengths { + if lineLen > maxLineLength { + maxLineLength = lineLen + } + } + + l.Log("\n") + l.Log(strings.Repeat("+", maxLineLength+8)) + for _, line := range lines { + l.Logf("++ %s ++\n", centerText(line, maxLineLength)) + } + l.Log(strings.Repeat("+", maxLineLength+8)) + l.Log("\n") +} + +// centerText: Centers text across a fixed width, filling either side with +// whitespace buffers +func centerText(text string, width int) string { + textLen := len(text) + leftBuffer := strings.Repeat(" ", (width-textLen)/2) + rightBuffer := strings.Repeat(" ", (width-textLen)/2+(width-textLen)%2) + + return fmt.Sprintf("%s%s%s", leftBuffer, text, rightBuffer) +} + +func (val *Validator) AssertERC20Balance( + contract gethcommon.Address, + accAddr gethcommon.Address, + expectedBalance *big.Int, + s *suite.Suite, +) { + input, err := embeds.SmartContract_ERC20Minter.ABI.Pack("balanceOf", accAddr) + s.NoError(err) + msg := geth.CallMsg{ + From: accAddr, + To: &contract, + Data: input, + } + recipientBalanceBeforeBytes, err := val.JSONRPCClient.CallContract(context.Background(), msg, nil) + s.NoError(err) + balance := new(big.Int).SetBytes(recipientBalanceBeforeBytes) + s.Equal(expectedBalance.String(), balance.String()) +} + +func (node *Validator) BlockByEthTx( + ethTxHash gethcommon.Hash, +) (*cmtcore.ResultBlockResults, error) { + blankCtx := context.Background() + txReceipt, err := node.JSONRPCClient.TransactionReceipt(blankCtx, ethTxHash) + if err != nil { + return nil, err + } + blockHeightOfTx := txReceipt.BlockNumber.Int64() + return node.RPCClient.BlockResults(blankCtx, &blockHeightOfTx) +} diff --git a/x/common/testutil/testutil_test.go b/x/common/testutil/testutil_test.go index ec08e2a07..405f6c0b6 100644 --- a/x/common/testutil/testutil_test.go +++ b/x/common/testutil/testutil_test.go @@ -11,8 +11,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/set" - "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" ) type TestSuite struct { diff --git a/x/devgas/v1/ante/ante.go b/x/devgas/v1/ante/ante.go index 1583138bd..697a46698 100644 --- a/x/devgas/v1/ante/ante.go +++ b/x/devgas/v1/ante/ante.go @@ -9,7 +9,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) var _ sdk.AnteDecorator = (*DevGasPayoutDecorator)(nil) diff --git a/x/devgas/v1/ante/ante_test.go b/x/devgas/v1/ante/ante_test.go index 9b9b00d43..75a974e04 100644 --- a/x/devgas/v1/ante/ante_test.go +++ b/x/devgas/v1/ante/ante_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "cosmossdk.io/math" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" sdkclienttx "github.com/cosmos/cosmos-sdk/client/tx" "github.com/stretchr/testify/suite" @@ -13,11 +14,11 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - devgasante "github.com/NibiruChain/nibiru/x/devgas/v1/ante" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + devgasante "github.com/NibiruChain/nibiru/v2/x/devgas/v1/ante" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) type AnteTestSuite struct { @@ -25,12 +26,13 @@ type AnteTestSuite struct { } func TestAnteSuite(t *testing.T) { + testapp.EnsureNibiruPrefix() suite.Run(t, new(AnteTestSuite)) } func (suite *AnteTestSuite) TestFeeLogic() { // We expect all to pass - feeCoins := sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(500)), sdk.NewCoin("utoken", sdk.NewInt(250))) + feeCoins := sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(500)), sdk.NewCoin("utoken", math.NewInt(250))) testCases := []struct { name string @@ -42,72 +44,72 @@ func (suite *AnteTestSuite) TestFeeLogic() { { "100% fee / 1 contract", feeCoins, - sdk.NewDecWithPrec(100, 2), + math.LegacyNewDecWithPrec(100, 2), 1, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(500)), sdk.NewCoin("utoken", sdk.NewInt(250))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(500)), sdk.NewCoin("utoken", math.NewInt(250))), }, { "100% fee / 2 contracts", feeCoins, - sdk.NewDecWithPrec(100, 2), + math.LegacyNewDecWithPrec(100, 2), 2, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(250)), sdk.NewCoin("utoken", sdk.NewInt(125))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(250)), sdk.NewCoin("utoken", math.NewInt(125))), }, { "100% fee / 10 contracts", feeCoins, - sdk.NewDecWithPrec(100, 2), + math.LegacyNewDecWithPrec(100, 2), 10, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(50)), sdk.NewCoin("utoken", sdk.NewInt(25))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(50)), sdk.NewCoin("utoken", math.NewInt(25))), }, { "67% fee / 7 contracts", feeCoins, - sdk.NewDecWithPrec(67, 2), + math.LegacyNewDecWithPrec(67, 2), 7, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(48)), sdk.NewCoin("utoken", sdk.NewInt(24))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(48)), sdk.NewCoin("utoken", math.NewInt(24))), }, { "50% fee / 1 contracts", feeCoins, - sdk.NewDecWithPrec(50, 2), + math.LegacyNewDecWithPrec(50, 2), 1, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(250)), sdk.NewCoin("utoken", sdk.NewInt(125))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(250)), sdk.NewCoin("utoken", math.NewInt(125))), }, { "50% fee / 2 contracts", feeCoins, - sdk.NewDecWithPrec(50, 2), + math.LegacyNewDecWithPrec(50, 2), 2, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(125)), sdk.NewCoin("utoken", sdk.NewInt(62))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(125)), sdk.NewCoin("utoken", math.NewInt(62))), }, { "50% fee / 3 contracts", feeCoins, - sdk.NewDecWithPrec(50, 2), + math.LegacyNewDecWithPrec(50, 2), 3, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(83)), sdk.NewCoin("utoken", sdk.NewInt(42))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(83)), sdk.NewCoin("utoken", math.NewInt(42))), }, { "25% fee / 2 contracts", feeCoins, - sdk.NewDecWithPrec(25, 2), + math.LegacyNewDecWithPrec(25, 2), 2, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(62)), sdk.NewCoin("utoken", sdk.NewInt(31))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(62)), sdk.NewCoin("utoken", math.NewInt(31))), }, { "15% fee / 3 contracts", feeCoins, - sdk.NewDecWithPrec(15, 2), + math.LegacyNewDecWithPrec(15, 2), 3, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(25)), sdk.NewCoin("utoken", sdk.NewInt(12))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(25)), sdk.NewCoin("utoken", math.NewInt(12))), }, { "1% fee / 2 contracts", feeCoins, - sdk.NewDecWithPrec(1, 2), + math.LegacyNewDecWithPrec(1, 2), 2, - sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(2)), sdk.NewCoin("utoken", sdk.NewInt(1))), + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(2)), sdk.NewCoin("utoken", math.NewInt(1))), }, } @@ -126,8 +128,8 @@ func (suite *AnteTestSuite) TestFeeLogic() { func (suite *AnteTestSuite) TestDevGasPayout() { txGasCoins := sdk.NewCoins( - sdk.NewCoin("unibi", sdk.NewInt(1_000)), - sdk.NewCoin("utoken", sdk.NewInt(500)), + sdk.NewCoin("unibi", math.NewInt(1_000)), + sdk.NewCoin("utoken", math.NewInt(500)), ) _, addrs := testutil.PrivKeyAddressPairs(11) @@ -166,7 +168,7 @@ func (suite *AnteTestSuite) TestDevGasPayout() { // The expected royalty is gas / num_withdrawers / 2. Thus, We // divide gas by (num_withdrawers * 2). The 2 comes from 50% split. // wantWithdrawerRoyalties: num_withdrawers * 2 = 2 - wantWithdrawerRoyalties: txGasCoins.QuoInt(sdk.NewInt(2)), + wantWithdrawerRoyalties: txGasCoins.QuoInt(math.NewInt(2)), wantErr: false, setup: func() (*app.NibiruApp, sdk.Context) { bapp, ctx := testapp.NewNibiruTestAppAndContext() @@ -187,7 +189,7 @@ func (suite *AnteTestSuite) TestDevGasPayout() { // The expected royalty is gas / num_withdrawers / 2. Thus, We // divide gas by (num_withdrawers * 2). The 2 comes from 50% split. // wantWithdrawerRoyalties: num_withdrawers * 2 = 4 - wantWithdrawerRoyalties: txGasCoins.QuoInt(sdk.NewInt(4)), + wantWithdrawerRoyalties: txGasCoins.QuoInt(math.NewInt(4)), wantErr: false, setup: func() (*app.NibiruApp, sdk.Context) { bapp, ctx := testapp.NewNibiruTestAppAndContext() @@ -205,7 +207,7 @@ func (suite *AnteTestSuite) TestDevGasPayout() { // The expected royalty is gas / num_withdrawers / 2. Thus, We // divide gas by (num_withdrawers * 2). The 2 comes from 50% split. // wantWithdrawerRoyalties: num_withdrawers * 2 = 2 - wantWithdrawerRoyalties: txGasCoins.QuoInt(sdk.NewInt(2)), + wantWithdrawerRoyalties: txGasCoins.QuoInt(math.NewInt(2)), wantErr: true, setup: func() (*app.NibiruApp, sdk.Context) { bapp, ctx := testapp.NewNibiruTestAppAndContext() @@ -218,7 +220,7 @@ func (suite *AnteTestSuite) TestDevGasPayout() { // The expected royalty is gas / num_withdrawers / 2. Thus, We // divide gas by (num_withdrawers * 2). The 2 comes from 50% split. // wantWithdrawerRoyalties: num_withdrawers * 2 = 2 - wantWithdrawerRoyalties: txGasCoins.QuoInt(sdk.NewInt(2)), + wantWithdrawerRoyalties: txGasCoins.QuoInt(math.NewInt(2)), wantErr: false, setup: func() (*app.NibiruApp, sdk.Context) { bapp, ctx := testapp.NewNibiruTestAppAndContext() @@ -234,7 +236,8 @@ func (suite *AnteTestSuite) TestDevGasPayout() { } for _, tc := range testCases { - suite.T().Run(tc.name, func(t *testing.T) { + suite.Run(tc.name, func() { + t := suite.T() bapp, ctx := tc.setup() ctx = ctx.WithChainID("mock-chain-id") anteDecorator := devgasante.NewDevGasPayoutDecorator( diff --git a/x/devgas/v1/ante/expected_keepers.go b/x/devgas/v1/ante/expected_keepers.go index eefe11c35..f961e9705 100644 --- a/x/devgas/v1/ante/expected_keepers.go +++ b/x/devgas/v1/ante/expected_keepers.go @@ -1,11 +1,11 @@ package ante -// Interfaces needed for the for the Nibiru Chain ante handler +// Interfaces needed for the Nibiru Chain ante handler import ( sdk "github.com/cosmos/cosmos-sdk/types" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) type BankKeeper interface { diff --git a/x/devgas/v1/client/cli/cli_test.go b/x/devgas/v1/client/cli/cli_test.go new file mode 100644 index 000000000..544948f95 --- /dev/null +++ b/x/devgas/v1/client/cli/cli_test.go @@ -0,0 +1,228 @@ +package cli_test + +import ( + "bytes" + "context" + "fmt" + "io" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + sdktestutil "github.com/cosmos/cosmos-sdk/testutil" + "github.com/stretchr/testify/suite" + + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + sdkclient "github.com/cosmos/cosmos-sdk/client" + sdktestutilcli "github.com/cosmos/cosmos-sdk/testutil/cli" + sdk "github.com/cosmos/cosmos-sdk/types" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + devgas "github.com/NibiruChain/nibiru/v2/x/devgas/v1" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/client/cli" +) + +// CLITestSuite: Tests all tx commands for the module. +type CLITestSuite struct { + suite.Suite + + keyring keyring.Keyring + encCfg testutilmod.TestEncodingConfig + baseCtx sdkclient.Context + clientCtx sdkclient.Context + + testAcc sdktestutil.TestAccount +} + +func TestCLITestSuite(t *testing.T) { + suite.Run(t, new(CLITestSuite)) +} + +// Runs once before the entire test suite. +func (s *CLITestSuite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(devgas.AppModuleBasic{}) + s.keyring = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = sdkclient.Context{}. + WithKeyring(s.keyring). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithClient(sdktestutilcli.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(sdkclient.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID("test-chain") + + var outBuf bytes.Buffer + ctxGen := func() sdkclient.Context { + bz, _ := s.encCfg.Codec.Marshal(&sdk.TxResponse{}) + c := sdktestutilcli.NewMockTendermintRPC(abci.ResponseQuery{ + Value: bz, + }) + return s.baseCtx.WithClient(c) + } + s.clientCtx = ctxGen().WithOutput(&outBuf) + + testAccs := sdktestutil.CreateKeyringAccounts(s.T(), s.keyring, 1) + s.testAcc = testAccs[0] +} + +// Flags for broadcasting transactions +func commonTxArgs() []string { + return []string{ + "--yes=true", // skip confirmation + "--broadcast-mode=sync", + "--fees=1unibi", + "--chain-id=test-chain", + } +} + +type TestCase struct { + name string + args []string + extraArgs []string + wantErr string +} + +func (tc TestCase) NewCtx(s *CLITestSuite) sdkclient.Context { + return s.baseCtx +} + +func (tc TestCase) Run(s *CLITestSuite) { + s.Run(tc.name, func() { + ctx := svrcmd.CreateExecuteContext(context.Background()) + + cmd := cli.NewTxCmd() + cmd.SetContext(ctx) + args := append(tc.args, commonTxArgs()...) + cmd.SetArgs(append(args, tc.extraArgs...)) + + s.Require().NoError(sdkclient.SetCmdClientContextHandler(tc.NewCtx(s), cmd)) + + err := cmd.Execute() + if tc.wantErr != "" { + s.Require().Error(err) + s.ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) +} + +func (s *CLITestSuite) TestCmdRegisterFeeShare() { + _, addrs := testutil.PrivKeyAddressPairs(3) + + testCases := []TestCase{ + { + name: "happy path: devgas register", + args: []string{"register", addrs[0].String(), addrs[1].String()}, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "", + }, + { + name: "sad: fee payer", + args: []string{"register", addrs[0].String(), addrs[1].String()}, + extraArgs: []string{ + fmt.Sprintf("--from=%s", s.testAcc.Address), + fmt.Sprintf("--fee-payer=%s", "invalid-fee-payer"), + }, + wantErr: "decoding bech32 failed", + }, + { + name: "sad: contract addr", + args: []string{"register", "sadcontract", addrs[1].String()}, + extraArgs: []string{ + fmt.Sprintf("--from=%s", s.testAcc.Address), + }, + wantErr: "invalid contract address", + }, + { + name: "sad: withdraw addr", + args: []string{"register", addrs[0].String(), "sadwithdraw"}, + extraArgs: []string{ + fmt.Sprintf("--from=%s", s.testAcc.Address), + }, + wantErr: "invalid withdraw address", + }, + } + + for _, tc := range testCases { + tc.Run(s) + } +} + +func (s *CLITestSuite) TestCmdCancelFeeShare() { + _, addrs := testutil.PrivKeyAddressPairs(1) + testCases := []TestCase{ + { + name: "happy path: devgas cancel", + args: []string{"cancel", addrs[0].String()}, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "", + }, + { + name: "sad: fee payer", + args: []string{"cancel", addrs[0].String()}, + extraArgs: []string{ + fmt.Sprintf("--from=%s", s.testAcc.Address), + fmt.Sprintf("--fee-payer=%s", "invalid-fee-payer"), + }, + wantErr: "decoding bech32 failed", + }, + { + name: "sad: contract addr", + args: []string{"cancel", "sadcontract"}, + extraArgs: []string{ + fmt.Sprintf("--from=%s", s.testAcc.Address), + }, + wantErr: "invalid deployer address", + }, + } + + for _, tc := range testCases { + tc.Run(s) + } +} + +func (s *CLITestSuite) TestCmdUpdateFeeShare() { + _, addrs := testutil.PrivKeyAddressPairs(3) + + testCases := []TestCase{ + { + name: "happy path: devgas update", + args: []string{"update", addrs[0].String(), addrs[1].String()}, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "", + }, + { + name: "sad: fee payer", + args: []string{"update", addrs[0].String(), addrs[1].String()}, + extraArgs: []string{ + fmt.Sprintf("--from=%s", s.testAcc.Address), + fmt.Sprintf("--fee-payer=%s", "invalid-fee-payer"), + }, + wantErr: "decoding bech32 failed", + }, + { + name: "sad: contract addr", + args: []string{"update", "sadcontract", addrs[1].String()}, + extraArgs: []string{ + fmt.Sprintf("--from=%s", s.testAcc.Address), + }, + wantErr: "invalid contract", + }, + { + name: "sad: new withdraw addr", + args: []string{"update", addrs[0].String(), "saddeployer"}, + extraArgs: []string{ + fmt.Sprintf("--from=%s", s.testAcc.Address), + }, + wantErr: "invalid withdraw address", + }, + } + + for _, tc := range testCases { + tc.Run(s) + } +} diff --git a/x/devgas/v1/client/cli/query.go b/x/devgas/v1/client/cli/query.go index 4dcda6481..740441331 100644 --- a/x/devgas/v1/client/cli/query.go +++ b/x/devgas/v1/client/cli/query.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/version" - "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) // GetQueryCmd returns the cli query commands for this module diff --git a/x/devgas/v1/client/cli/tx.go b/x/devgas/v1/client/cli/tx.go index e059af0dc..f5fb65065 100644 --- a/x/devgas/v1/client/cli/tx.go +++ b/x/devgas/v1/client/cli/tx.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) // NewTxCmd returns a root CLI command handler for certain modules/FeeShare diff --git a/x/devgas/v1/genesis.go b/x/devgas/v1/genesis.go index f0e877ec2..7b95912ed 100644 --- a/x/devgas/v1/genesis.go +++ b/x/devgas/v1/genesis.go @@ -5,8 +5,8 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/devgas/v1/keeper" - "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) // InitGenesis import module genesis diff --git a/x/devgas/v1/genesis_test.go b/x/devgas/v1/genesis_test.go index b1c673e30..7c450f608 100644 --- a/x/devgas/v1/genesis_test.go +++ b/x/devgas/v1/genesis_test.go @@ -4,15 +4,16 @@ import ( "fmt" "testing" + "cosmossdk.io/math" "github.com/stretchr/testify/suite" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - devgas "github.com/NibiruChain/nibiru/x/devgas/v1" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + devgas "github.com/NibiruChain/nibiru/v2/x/devgas/v1" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) type GenesisTestSuite struct { @@ -65,7 +66,7 @@ func (s *GenesisTestSuite) TestGenesis() { genesis: devgastypes.GenesisState{ Params: devgastypes.ModuleParams{ EnableFeeShare: true, - DeveloperShares: sdk.NewDecWithPrec(0, 2), + DeveloperShares: math.LegacyNewDecWithPrec(0, 2), AllowedDenoms: []string{"unibi"}, }, }, @@ -76,7 +77,7 @@ func (s *GenesisTestSuite) TestGenesis() { genesis: devgastypes.GenesisState{ Params: devgastypes.ModuleParams{ EnableFeeShare: true, - DeveloperShares: sdk.NewDecWithPrec(100, 2), + DeveloperShares: math.LegacyNewDecWithPrec(100, 2), AllowedDenoms: []string{"unibi"}, }, }, @@ -87,7 +88,7 @@ func (s *GenesisTestSuite) TestGenesis() { genesis: devgastypes.GenesisState{ Params: devgastypes.ModuleParams{ EnableFeeShare: true, - DeveloperShares: sdk.NewDecWithPrec(10, 2), + DeveloperShares: math.LegacyNewDecWithPrec(10, 2), AllowedDenoms: []string{}, }, FeeShare: []devgastypes.FeeShare{ @@ -108,7 +109,7 @@ func (s *GenesisTestSuite) TestGenesis() { } for _, tc := range testCases { - s.T().Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) { + s.Run(fmt.Sprintf("Case %s", tc.name), func() { s.SetupTest() // reset if tc.expPanic { diff --git a/x/devgas/v1/keeper/feeshare.go b/x/devgas/v1/keeper/feeshare.go index cd1493469..477a481f8 100644 --- a/x/devgas/v1/keeper/feeshare.go +++ b/x/devgas/v1/keeper/feeshare.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) // GetFeeShare returns the FeeShare for a registered contract diff --git a/x/devgas/v1/keeper/grpc_query.go b/x/devgas/v1/keeper/grpc_query.go index ae7a5a6c9..5a668cc94 100644 --- a/x/devgas/v1/keeper/grpc_query.go +++ b/x/devgas/v1/keeper/grpc_query.go @@ -8,7 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) var _ types.QueryServer = Querier{} diff --git a/x/devgas/v1/keeper/grpc_query_test.go b/x/devgas/v1/keeper/grpc_query_test.go index cee8cd6ff..a48ec9b74 100644 --- a/x/devgas/v1/keeper/grpc_query_test.go +++ b/x/devgas/v1/keeper/grpc_query_test.go @@ -4,16 +4,18 @@ import ( "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/testutil" - devgaskeeper "github.com/NibiruChain/nibiru/x/devgas/v1/keeper" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "cosmossdk.io/math" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + devgaskeeper "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) -func (s *IntegrationTestSuite) TestQueryFeeShares() { +func (s *KeeperTestSuite) TestQueryFeeShares() { s.SetupTest() _, _, sender := testdata.KeyTestPubAddr() _ = s.FundAccount( - s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000))), + s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1_000_000))), ) _, _, withdrawer := testdata.KeyTestPubAddr() @@ -70,10 +72,10 @@ func (s *IntegrationTestSuite) TestQueryFeeShares() { }) } -func (s *IntegrationTestSuite) TestFeeShare() { +func (s *KeeperTestSuite) TestFeeShare() { s.SetupTest() _, _, sender := testdata.KeyTestPubAddr() - _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1_000_000)))) _, _, withdrawer := testdata.KeyTestPubAddr() @@ -102,10 +104,10 @@ func (s *IntegrationTestSuite) TestFeeShare() { s.Require().Equal(resp.Feeshare, feeShare) } -func (s *IntegrationTestSuite) TestFeeSharesByWithdrawer() { +func (s *KeeperTestSuite) TestFeeSharesByWithdrawer() { s.SetupTest() _, _, sender := testdata.KeyTestPubAddr() - _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1_000_000)))) _, _, withdrawer := testdata.KeyTestPubAddr() @@ -141,7 +143,7 @@ func (s *IntegrationTestSuite) TestFeeSharesByWithdrawer() { }) } -func (s *IntegrationTestSuite) TestQueryParams() { +func (s *KeeperTestSuite) TestQueryParams() { s.SetupTest() goCtx := sdk.WrapSDKContext(s.ctx) resp, err := s.queryClient.Params(goCtx, nil) @@ -149,7 +151,7 @@ func (s *IntegrationTestSuite) TestQueryParams() { s.NotNil(resp) } -func (s *IntegrationTestSuite) TestNilRequests() { +func (s *KeeperTestSuite) TestNilRequests() { s.SetupTest() goCtx := sdk.WrapSDKContext(s.ctx) querier := devgaskeeper.NewQuerier(s.app.DevGasKeeper) diff --git a/x/devgas/v1/keeper/keeper.go b/x/devgas/v1/keeper/keeper.go index 97081ccd2..83ce8e770 100644 --- a/x/devgas/v1/keeper/keeper.go +++ b/x/devgas/v1/keeper/keeper.go @@ -13,7 +13,7 @@ import ( "github.com/NibiruChain/collections" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) // Keeper of this module maintains collections of feeshares for contracts @@ -26,7 +26,7 @@ type Keeper struct { wasmKeeper wasmkeeper.Keeper accountKeeper devgastypes.AccountKeeper - // feeCollectorName is the name of of x/auth module's fee collector module + // feeCollectorName is the name of x/auth module's fee collector module // account, "fee_collector", which collects transaction fees for distribution // to all stakers. // diff --git a/x/devgas/v1/keeper/keeper_test.go b/x/devgas/v1/keeper/keeper_test.go index 22c3806b1..9dd4382ab 100644 --- a/x/devgas/v1/keeper/keeper_test.go +++ b/x/devgas/v1/keeper/keeper_test.go @@ -10,10 +10,10 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - devgaskeeper "github.com/NibiruChain/nibiru/x/devgas/v1/keeper" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + devgaskeeper "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) // BankKeeper defines the expected interface needed to retrieve account balances. @@ -25,7 +25,7 @@ type BankKeeper interface { SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error } -type IntegrationTestSuite struct { +type KeeperTestSuite struct { suite.Suite ctx sdk.Context @@ -35,7 +35,7 @@ type IntegrationTestSuite struct { wasmMsgServer wasmtypes.MsgServer } -func (s *IntegrationTestSuite) SetupTest() { +func (s *KeeperTestSuite) SetupTest() { nibiruApp, ctx := testapp.NewNibiruTestAppAndContext() s.app = nibiruApp s.ctx = ctx @@ -52,16 +52,16 @@ func (s *IntegrationTestSuite) SetupTest() { s.wasmMsgServer = wasmkeeper.NewMsgServerImpl(&s.app.WasmKeeper) } -func (s *IntegrationTestSuite) SetupSuite() { +func (s *KeeperTestSuite) SetupSuite() { s.SetupTest() } -func (s *IntegrationTestSuite) FundAccount( +func (s *KeeperTestSuite) FundAccount( ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins, ) error { return testapp.FundAccount(s.app.BankKeeper, ctx, addr, amounts) } func TestKeeperTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) + suite.Run(t, new(KeeperTestSuite)) } diff --git a/x/devgas/v1/keeper/msg_server.go b/x/devgas/v1/keeper/msg_server.go index 0976557e4..50cd7e984 100644 --- a/x/devgas/v1/keeper/msg_server.go +++ b/x/devgas/v1/keeper/msg_server.go @@ -9,7 +9,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) var _ types.MsgServer = &Keeper{} diff --git a/x/devgas/v1/keeper/msg_server_test.go b/x/devgas/v1/keeper/msg_server_test.go index 68b7e34a1..d79f1e5cf 100644 --- a/x/devgas/v1/keeper/msg_server_test.go +++ b/x/devgas/v1/keeper/msg_server_test.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "fmt" + "cosmossdk.io/math" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -14,13 +15,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) //go:embed testdata/reflect.wasm var wasmContract []byte -func (s *IntegrationTestSuite) StoreCode() { +func (s *KeeperTestSuite) StoreCode() { _, _, sender := testdata.KeyTestPubAddr() msg := wasmtypes.MsgStoreCodeFixture(func(m *wasmtypes.MsgStoreCode) { m.WASMByteCode = wasmContract @@ -41,7 +42,7 @@ func (s *IntegrationTestSuite) StoreCode() { s.Require().Equal(wasmtypes.DefaultParams().InstantiateDefaultPermission.With(sender), info.InstantiateConfig) } -func (s *IntegrationTestSuite) InstantiateContract(sender string, admin string) string { +func (s *KeeperTestSuite) InstantiateContract(sender string, admin string) string { msgStoreCode := wasmtypes.MsgStoreCodeFixture(func(m *wasmtypes.MsgStoreCode) { m.WASMByteCode = wasmContract m.Sender = sender @@ -66,11 +67,11 @@ func (s *IntegrationTestSuite) InstantiateContract(sender string, admin string) return result.Address } -func (s *IntegrationTestSuite) TestGetContractAdminOrCreatorAddress() { +func (s *KeeperTestSuite) TestGetContractAdminOrCreatorAddress() { _, _, sender := testdata.KeyTestPubAddr() _, _, admin := testdata.KeyTestPubAddr() - _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) - _ = s.FundAccount(s.ctx, admin, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1_000_000)))) + _ = s.FundAccount(s.ctx, admin, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1_000_000)))) noAdminContractAddress := s.InstantiateContract(sender.String(), "") withAdminContractAddress := s.InstantiateContract(sender.String(), admin.String()) @@ -117,9 +118,9 @@ func (s *IntegrationTestSuite) TestGetContractAdminOrCreatorAddress() { } } -func (s *IntegrationTestSuite) TestRegisterFeeShare() { +func (s *KeeperTestSuite) TestRegisterFeeShare() { _, _, sender := testdata.KeyTestPubAddr() - _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1_000_000)))) gov := s.app.AccountKeeper.GetModuleAddress(govtypes.ModuleName).String() govContract := s.InstantiateContract(sender.String(), gov) @@ -245,9 +246,9 @@ func (s *IntegrationTestSuite) TestRegisterFeeShare() { } } -func (s *IntegrationTestSuite) TestUpdateFeeShare() { +func (s *KeeperTestSuite) TestUpdateFeeShare() { _, _, sender := testdata.KeyTestPubAddr() - _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1_000_000)))) fmt.Printf("s.ctx: %v\n", s.ctx) fmt.Printf("s.ctx.BlockTime(): %v\n", s.ctx.BlockTime()) @@ -342,9 +343,9 @@ func (s *IntegrationTestSuite) TestUpdateFeeShare() { } } -func (s *IntegrationTestSuite) TestCancelFeeShare() { +func (s *KeeperTestSuite) TestCancelFeeShare() { _, _, sender := testdata.KeyTestPubAddr() - _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(1_000_000)))) contractAddress := s.InstantiateContract(sender.String(), "") _, _, withdrawer := testdata.KeyTestPubAddr() @@ -409,7 +410,7 @@ func (s *IntegrationTestSuite) TestCancelFeeShare() { } } -func (s *IntegrationTestSuite) TestUpdateParams() { +func (s *KeeperTestSuite) TestUpdateParams() { govModuleAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() goCtx := sdk.WrapSDKContext(s.ctx) diff --git a/x/devgas/v1/keeper/params.go b/x/devgas/v1/keeper/params.go index 3760136e5..658558ae0 100644 --- a/x/devgas/v1/keeper/params.go +++ b/x/devgas/v1/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) // GetParams returns the total set of fees parameters. diff --git a/x/devgas/v1/keeper/store.go b/x/devgas/v1/keeper/store.go index 93a2ff2db..9327504bf 100644 --- a/x/devgas/v1/keeper/store.go +++ b/x/devgas/v1/keeper/store.go @@ -5,7 +5,7 @@ import ( sdkcodec "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) type DevGasIndexes struct { diff --git a/x/devgas/v1/module.go b/x/devgas/v1/module.go index ce2b011b3..8176c0360 100644 --- a/x/devgas/v1/module.go +++ b/x/devgas/v1/module.go @@ -5,28 +5,29 @@ import ( "encoding/json" "fmt" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/spf13/cobra" - abci "github.com/cometbft/cometbft/abci/types" - sdkclient "github.com/cosmos/cosmos-sdk/client" sdkcodec "github.com/cosmos/cosmos-sdk/codec" sdkcodectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" - devgascli "github.com/NibiruChain/nibiru/x/devgas/v1/client/cli" - devgasexported "github.com/NibiruChain/nibiru/x/devgas/v1/exported" - devgaskeeper "github.com/NibiruChain/nibiru/x/devgas/v1/keeper" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/client/cli" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/exported" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/keeper" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/simulation" + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) // type check to ensure the interface is properly implemented var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} ) // ConsensusVersion defines the current module consensus version. @@ -37,13 +38,13 @@ type AppModuleBasic struct{} // Name returns the fees module's name. func (AppModuleBasic) Name() string { - return devgastypes.ModuleName + return types.ModuleName } // RegisterLegacyAminoCodec performs a no-op as the fees do not support Amino // encoding. func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *sdkcodec.LegacyAmino) { - devgastypes.RegisterLegacyAminoCodec(cdc) + types.RegisterLegacyAminoCodec(cdc) } // ConsensusVersion returns the consensus state-breaking version for the module. @@ -56,22 +57,22 @@ func (AppModuleBasic) ConsensusVersion() uint64 { func (AppModuleBasic) RegisterInterfaces( interfaceRegistry sdkcodectypes.InterfaceRegistry, ) { - devgastypes.RegisterInterfaces(interfaceRegistry) + types.RegisterInterfaces(interfaceRegistry) } // DefaultGenesis returns default genesis state as raw bytes for the fees // module. func (AppModuleBasic) DefaultGenesis(cdc sdkcodec.JSONCodec) json.RawMessage { - return cdc.MustMarshalJSON(devgastypes.DefaultGenesisState()) + return cdc.MustMarshalJSON(types.DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the fees module. func (b AppModuleBasic) ValidateGenesis( cdc sdkcodec.JSONCodec, _ sdkclient.TxEncodingConfig, bz json.RawMessage, ) error { - var genesisState devgastypes.GenesisState + var genesisState types.GenesisState if err := cdc.UnmarshalJSON(bz, &genesisState); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", devgastypes.ModuleName, err) + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } return genesisState.Validate() @@ -82,19 +83,19 @@ func (b AppModuleBasic) ValidateGenesis( func (b AppModuleBasic) RegisterGRPCGatewayRoutes( c sdkclient.Context, serveMux *runtime.ServeMux, ) { - if err := devgastypes.RegisterQueryHandlerClient(context.Background(), serveMux, devgastypes.NewQueryClient(c)); err != nil { + if err := types.RegisterQueryHandlerClient(context.Background(), serveMux, types.NewQueryClient(c)); err != nil { panic(err) } } // GetTxCmd returns the root tx command for the fees module. func (AppModuleBasic) GetTxCmd() *cobra.Command { - return devgascli.NewTxCmd() + return cli.NewTxCmd() } // GetQueryCmd returns the fees module's root query command. func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return devgascli.GetQueryCmd() + return cli.GetQueryCmd() } // ___________________________________________________________________________ @@ -102,18 +103,18 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { // AppModule implements the AppModule interface for the fees module. type AppModule struct { AppModuleBasic - keeper devgaskeeper.Keeper + keeper keeper.Keeper ak authkeeper.AccountKeeper // legacySubspace is used solely for migration of x/params managed parameters - legacySubspace devgasexported.Subspace + legacySubspace exported.Subspace } // NewAppModule creates a new AppModule Object func NewAppModule( - k devgaskeeper.Keeper, + k keeper.Keeper, ak authkeeper.AccountKeeper, - ss devgasexported.Subspace, + ss exported.Subspace, ) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, @@ -125,7 +126,7 @@ func NewAppModule( // Name returns the fees module's name. func (AppModule) Name() string { - return devgastypes.ModuleName + return types.ModuleName } // RegisterInvariants registers the fees module's invariants. @@ -133,15 +134,15 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // QuerierRoute returns the module's query routing key. func (am AppModule) QuerierRoute() string { - return devgastypes.RouterKey + return types.RouterKey } // RegisterServices registers a GRPC query service to respond to the // module-specific GRPC queries. func (am AppModule) RegisterServices(cfg module.Configurator) { - devgastypes.RegisterMsgServer(cfg.MsgServer(), am.keeper) - devgastypes.RegisterQueryServer( - cfg.QueryServer(), devgaskeeper.NewQuerier(am.keeper), + types.RegisterMsgServer(cfg.MsgServer(), am.keeper) + types.RegisterQueryServer( + cfg.QueryServer(), keeper.NewQuerier(am.keeper), ) } @@ -158,7 +159,7 @@ func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Valid // InitGenesis performs the fees module's genesis initialization. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, cdc sdkcodec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState devgastypes.GenesisState + var genesisState types.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) InitGenesis(ctx, am.keeper, genesisState) @@ -170,3 +171,21 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc sdkcodec.JSONCodec) json. gs := ExportGenesis(ctx, am.keeper) return cdc.MustMarshalJSON(gs) } + +//---------------------------------------------------------------------------- +// AppModuleSimulation functions +//---------------------------------------------------------------------------- + +// GenerateGenesisState implements module.AppModuleSimulation. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RegisterStoreDecoder implements module.AppModuleSimulation. +func (AppModule) RegisterStoreDecoder(sdk.StoreDecoderRegistry) { +} + +// WeightedOperations implements module.AppModuleSimulation. +func (AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return nil +} diff --git a/x/devgas/v1/module_test.go b/x/devgas/v1/module_test.go index 4ab9d8929..7723c59c0 100644 --- a/x/devgas/v1/module_test.go +++ b/x/devgas/v1/module_test.go @@ -3,9 +3,9 @@ package devgas_test import ( abci "github.com/cometbft/cometbft/abci/types" - "github.com/NibiruChain/nibiru/app/codec" - devgas "github.com/NibiruChain/nibiru/x/devgas/v1" - devgastypes "github.com/NibiruChain/nibiru/x/devgas/v1/types" + "github.com/NibiruChain/nibiru/v2/app/codec" + devgas "github.com/NibiruChain/nibiru/v2/x/devgas/v1" + devgastypes "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" ) func (s *GenesisTestSuite) TestAppModule() { diff --git a/x/devgas/v1/simulation/genesis.go b/x/devgas/v1/simulation/genesis.go new file mode 100644 index 000000000..a32fa8cbd --- /dev/null +++ b/x/devgas/v1/simulation/genesis.go @@ -0,0 +1,47 @@ +package simulation + +// DONTCOVER + +import ( + "encoding/json" + "fmt" + "math/rand" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/NibiruChain/nibiru/v2/x/devgas/v1/types" +) + +const ( + DeveloperFeeShare = "developer_fee_share" +) + +func GenDeveloperFeeShare(r *rand.Rand) sdk.Dec { + return math.LegacyNewDecWithPrec(int64(r.Intn(100)), 2) +} + +func RandomizedGenState(simState *module.SimulationState) { + var developerFeeShare sdk.Dec + simState.AppParams.GetOrGenerate( + simState.Cdc, DeveloperFeeShare, &developerFeeShare, simState.Rand, + func(r *rand.Rand) { developerFeeShare = GenDeveloperFeeShare(r) }, + ) + + devgasGenesis := types.GenesisState{ + Params: types.ModuleParams{ + EnableFeeShare: true, + DeveloperShares: developerFeeShare, + AllowedDenoms: []string{}, + }, + FeeShare: []types.FeeShare{}, + } + + bz, err := json.MarshalIndent(&devgasGenesis, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated x/devgas parameters:\n%s\n", bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&devgasGenesis) +} diff --git a/x/devgas/v1/types/devgas.pb.go b/x/devgas/v1/types/devgas.pb.go index 8d007c067..bbcf512e8 100644 --- a/x/devgas/v1/types/devgas.pb.go +++ b/x/devgas/v1/types/devgas.pb.go @@ -97,7 +97,7 @@ func init() { func init() { proto.RegisterFile("nibiru/devgas/v1/devgas.proto", fileDescriptor_f71dc4524d1e4ffb) } var fileDescriptor_f71dc4524d1e4ffb = []byte{ - // 210 bytes of a gzipped FileDescriptorProto + // 213 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcd, 0xcb, 0x4c, 0xca, 0x2c, 0x2a, 0xd5, 0x4f, 0x49, 0x2d, 0x4b, 0x4f, 0x2c, 0xd6, 0x2f, 0x33, 0x84, 0xb2, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x04, 0x20, 0xd2, 0x7a, 0x50, 0xc1, 0x32, 0x43, 0xa5, 0x7e, 0x46, @@ -106,12 +106,12 @@ var fileDescriptor_f71dc4524d1e4ffb = []byte{ 0x05, 0x46, 0x0d, 0xce, 0x20, 0x7e, 0x98, 0xb8, 0x23, 0x44, 0x18, 0xa4, 0x34, 0x25, 0xb5, 0x20, 0x27, 0xbf, 0x32, 0xb5, 0x08, 0xae, 0x94, 0x09, 0xa2, 0x14, 0x26, 0x0e, 0x53, 0xaa, 0xcb, 0x25, 0x54, 0x9e, 0x59, 0x92, 0x91, 0x52, 0x94, 0x58, 0x8e, 0xa4, 0x98, 0x19, 0xac, 0x58, 0x10, 0x21, - 0x03, 0x55, 0xee, 0xe4, 0x79, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, - 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xfa, + 0x03, 0x55, 0xee, 0xe4, 0x73, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, + 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x46, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x7e, 0x60, 0x8f, 0x38, 0x67, - 0x24, 0x66, 0xe6, 0xe9, 0x43, 0xfd, 0x5c, 0x81, 0xe4, 0xeb, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, - 0x36, 0xb0, 0xaf, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x2b, 0xbb, 0x00, 0xe8, 0x16, 0x01, - 0x00, 0x00, + 0x24, 0x66, 0xe6, 0xe9, 0x43, 0xfd, 0x5c, 0x66, 0xa4, 0x5f, 0x81, 0xe4, 0xf1, 0x92, 0xca, 0x82, + 0xd4, 0xe2, 0x24, 0x36, 0xb0, 0xc7, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x1b, 0x82, + 0x7a, 0x19, 0x01, 0x00, 0x00, } func (m *FeeShare) Marshal() (dAtA []byte, err error) { diff --git a/x/devgas/v1/types/event.pb.go b/x/devgas/v1/types/event.pb.go index 6bc5c33c4..dcb56d85c 100644 --- a/x/devgas/v1/types/event.pb.go +++ b/x/devgas/v1/types/event.pb.go @@ -274,7 +274,7 @@ func init() { func init() { proto.RegisterFile("nibiru/devgas/v1/event.proto", fileDescriptor_dd3ce94d3a226edf) } var fileDescriptor_dd3ce94d3a226edf = []byte{ - // 256 bytes of a gzipped FileDescriptorProto + // 259 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xc9, 0xcb, 0x4c, 0xca, 0x2c, 0x2a, 0xd5, 0x4f, 0x49, 0x2d, 0x4b, 0x4f, 0x2c, 0xd6, 0x2f, 0x33, 0xd4, 0x4f, 0x2d, 0x4b, 0xcd, 0x2b, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0xc8, 0xea, 0x41, 0x64, 0xf5, @@ -286,11 +286,12 @@ var fileDescriptor_dd3ce94d3a226edf = []byte{ 0x79, 0x73, 0x09, 0x82, 0xad, 0x73, 0x4e, 0xcc, 0x4b, 0x4e, 0xcd, 0xa1, 0xcc, 0x32, 0xa5, 0x6c, 0xa8, 0x61, 0xa1, 0x05, 0x29, 0x89, 0x25, 0xa9, 0x34, 0x76, 0xb9, 0x2e, 0xd4, 0xb2, 0x80, 0xc4, 0xca, 0xfc, 0xd2, 0x12, 0xa8, 0x65, 0x12, 0x5c, 0xec, 0x05, 0x60, 0x7e, 0x31, 0xd4, 0x2e, 0x18, - 0xd7, 0xc9, 0xf3, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, - 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xf4, 0xd3, 0x33, + 0xd7, 0xc9, 0xe7, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, + 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x8c, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0xfd, 0xc0, 0xd1, 0xe1, 0x9c, 0x91, 0x98, - 0x99, 0xa7, 0x0f, 0x8d, 0xb8, 0x0a, 0xa4, 0xa8, 0x2b, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, - 0xc7, 0x9d, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x32, 0xd6, 0x7e, 0xdb, 0x01, 0x00, 0x00, + 0x99, 0xa7, 0x0f, 0x8d, 0xb8, 0x32, 0x23, 0xfd, 0x0a, 0xa4, 0xd8, 0x2b, 0xa9, 0x2c, 0x48, 0x2d, + 0x4e, 0x62, 0x03, 0x47, 0x9f, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x57, 0x64, 0x78, 0xde, + 0x01, 0x00, 0x00, } func (m *EventRegisterDevGas) Marshal() (dAtA []byte, err error) { diff --git a/x/devgas/v1/types/genesis.pb.go b/x/devgas/v1/types/genesis.pb.go index d31dd8b53..7f0056694 100644 --- a/x/devgas/v1/types/genesis.pb.go +++ b/x/devgas/v1/types/genesis.pb.go @@ -148,30 +148,30 @@ func init() { func init() { proto.RegisterFile("nibiru/devgas/v1/genesis.proto", fileDescriptor_86a5066ce5bd7311) } var fileDescriptor_86a5066ce5bd7311 = []byte{ - // 358 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0xb1, 0x6e, 0xe2, 0x40, - 0x10, 0x86, 0xbd, 0x70, 0x42, 0xb0, 0x70, 0x1c, 0xb2, 0xae, 0xb0, 0x90, 0x6e, 0xb1, 0x90, 0xee, - 0xe4, 0xe6, 0x76, 0x05, 0xd7, 0x5e, 0x1a, 0x82, 0x12, 0xa5, 0x48, 0x14, 0x99, 0x2a, 0x69, 0xd0, - 0x1a, 0x0f, 0xc6, 0x8a, 0xed, 0xb5, 0xbc, 0xc6, 0x49, 0x9e, 0x21, 0x4d, 0x9e, 0x27, 0x4f, 0x40, - 0x49, 0x19, 0xa5, 0x40, 0x11, 0xbc, 0x48, 0xe4, 0xb5, 0x93, 0xa0, 0x50, 0x79, 0xfc, 0xcf, 0xfc, - 0xdf, 0x8c, 0xf6, 0xc7, 0x24, 0xf2, 0x1d, 0x3f, 0x59, 0x32, 0x17, 0x32, 0x8f, 0x4b, 0x96, 0x0d, - 0x98, 0x07, 0x11, 0x48, 0x5f, 0xd2, 0x38, 0x11, 0xa9, 0xd0, 0x3b, 0x45, 0x9f, 0x16, 0x7d, 0x9a, - 0x0d, 0xba, 0xbf, 0x0e, 0x1c, 0x65, 0x4f, 0x19, 0xba, 0x3f, 0x3d, 0xe1, 0x09, 0x55, 0xb2, 0xbc, - 0x2a, 0xd4, 0xfe, 0x03, 0xc2, 0xad, 0xd3, 0x02, 0x3c, 0x49, 0x79, 0x0a, 0xfa, 0x7f, 0x5c, 0x8b, - 0x79, 0xc2, 0x43, 0x69, 0x20, 0x13, 0x59, 0xcd, 0x21, 0xa1, 0x5f, 0x17, 0xd1, 0x73, 0xe1, 0x2e, - 0x03, 0xb8, 0x54, 0x53, 0xa3, 0x6f, 0xab, 0x4d, 0x4f, 0xb3, 0x4b, 0x8f, 0x7e, 0x84, 0x1b, 0x73, - 0x80, 0xa9, 0x5c, 0xf0, 0x04, 0x8c, 0x8a, 0x59, 0xb5, 0x9a, 0xc3, 0xee, 0x21, 0xe0, 0x04, 0x60, - 0x92, 0x4f, 0x94, 0xe6, 0xfa, 0xbc, 0xfc, 0xef, 0x3f, 0x21, 0xdc, 0xda, 0xa7, 0xeb, 0x16, 0xee, - 0x40, 0xc4, 0x9d, 0x00, 0xa6, 0x9f, 0xd8, 0xfc, 0xae, 0xba, 0xdd, 0x2e, 0xf4, 0x77, 0x94, 0x7e, - 0x85, 0x3b, 0x2e, 0x64, 0x10, 0x88, 0x18, 0x92, 0x62, 0x50, 0x1a, 0x15, 0x13, 0x59, 0x8d, 0x11, - 0xcd, 0x97, 0xbc, 0x6c, 0x7a, 0x7f, 0x3c, 0x3f, 0x5d, 0x2c, 0x1d, 0x3a, 0x13, 0x21, 0x9b, 0x09, - 0x19, 0x0a, 0x59, 0x7e, 0xfe, 0x4a, 0xf7, 0x86, 0xa5, 0xf7, 0x31, 0x48, 0x3a, 0x86, 0x99, 0xfd, - 0xe3, 0x83, 0xa3, 0xc8, 0x52, 0xff, 0x8d, 0xdb, 0x3c, 0x08, 0xc4, 0x2d, 0xb8, 0x53, 0x17, 0x22, - 0x11, 0x4a, 0xa3, 0x6a, 0x56, 0xad, 0x86, 0xfd, 0xbd, 0x54, 0xc7, 0x4a, 0x1c, 0x9d, 0xad, 0xb6, - 0x04, 0xad, 0xb7, 0x04, 0xbd, 0x6e, 0x09, 0x7a, 0xdc, 0x11, 0x6d, 0xbd, 0x23, 0xda, 0xf3, 0x8e, - 0x68, 0xd7, 0x6c, 0x6f, 0xf3, 0x85, 0x7a, 0x8c, 0xe3, 0x05, 0xf7, 0x23, 0x56, 0x06, 0x76, 0xb7, - 0x17, 0x99, 0x3a, 0xc3, 0xa9, 0xa9, 0x70, 0xfe, 0xbd, 0x05, 0x00, 0x00, 0xff, 0xff, 0x24, 0xf2, - 0x7e, 0x8d, 0x05, 0x02, 0x00, 0x00, + // 361 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0xc1, 0x6e, 0xda, 0x40, + 0x10, 0x86, 0xbd, 0x50, 0x21, 0x58, 0x28, 0x45, 0x56, 0x0f, 0x16, 0x52, 0x17, 0x0b, 0xa9, 0x95, + 0x2f, 0xdd, 0x15, 0xee, 0xb5, 0xbd, 0x50, 0xd4, 0x5e, 0x92, 0x28, 0x32, 0xa7, 0xe4, 0x82, 0xd6, + 0x78, 0x30, 0x56, 0x6c, 0xaf, 0xe5, 0x35, 0x4e, 0xf2, 0x0c, 0xb9, 0xe4, 0x79, 0xf2, 0x04, 0x1c, + 0x39, 0x46, 0x39, 0xa0, 0x08, 0x5e, 0x24, 0xf2, 0xda, 0x49, 0x50, 0x38, 0x79, 0xfc, 0xcf, 0xfc, + 0xdf, 0x8c, 0xf6, 0xc7, 0x24, 0x0e, 0xdc, 0x20, 0x5d, 0x31, 0x0f, 0x72, 0x9f, 0x4b, 0x96, 0x8f, + 0x98, 0x0f, 0x31, 0xc8, 0x40, 0xd2, 0x24, 0x15, 0x99, 0xd0, 0x7b, 0x65, 0x9f, 0x96, 0x7d, 0x9a, + 0x8f, 0xfa, 0xdf, 0x8e, 0x1c, 0x55, 0x4f, 0x19, 0xfa, 0x5f, 0x7d, 0xe1, 0x0b, 0x55, 0xb2, 0xa2, + 0x2a, 0xd5, 0xe1, 0x1d, 0xc2, 0x9d, 0xff, 0x25, 0x78, 0x9a, 0xf1, 0x0c, 0xf4, 0xdf, 0xb8, 0x91, + 0xf0, 0x94, 0x47, 0xd2, 0x40, 0x26, 0xb2, 0xda, 0x36, 0xa1, 0x1f, 0x17, 0xd1, 0x53, 0xe1, 0xad, + 0x42, 0x38, 0x57, 0x53, 0xe3, 0x4f, 0xeb, 0xed, 0x40, 0x73, 0x2a, 0x8f, 0xfe, 0x07, 0xb7, 0x16, + 0x00, 0x33, 0xb9, 0xe4, 0x29, 0x18, 0x35, 0xb3, 0x6e, 0xb5, 0xed, 0xfe, 0x31, 0xe0, 0x1f, 0xc0, + 0xb4, 0x98, 0xa8, 0xcc, 0xcd, 0x45, 0xf5, 0x3f, 0x7c, 0x40, 0xb8, 0x73, 0x48, 0xd7, 0x2d, 0xdc, + 0x83, 0x98, 0xbb, 0x21, 0xcc, 0xde, 0xb1, 0xc5, 0x5d, 0x4d, 0xa7, 0x5b, 0xea, 0xaf, 0x28, 0xfd, + 0x02, 0xf7, 0x3c, 0xc8, 0x21, 0x14, 0x09, 0xa4, 0xe5, 0xa0, 0x34, 0x6a, 0x26, 0xb2, 0x5a, 0x63, + 0x5a, 0x2c, 0x79, 0xda, 0x0e, 0x7e, 0xf8, 0x41, 0xb6, 0x5c, 0xb9, 0x74, 0x2e, 0x22, 0x36, 0x17, + 0x32, 0x12, 0xb2, 0xfa, 0xfc, 0x94, 0xde, 0x15, 0xcb, 0x6e, 0x13, 0x90, 0x74, 0x02, 0x73, 0xe7, + 0xcb, 0x1b, 0x47, 0x91, 0xa5, 0xfe, 0x1d, 0x77, 0x79, 0x18, 0x8a, 0x6b, 0xf0, 0x66, 0x1e, 0xc4, + 0x22, 0x92, 0x46, 0xdd, 0xac, 0x5b, 0x2d, 0xe7, 0x73, 0xa5, 0x4e, 0x94, 0x38, 0x3e, 0x59, 0xef, + 0x08, 0xda, 0xec, 0x08, 0x7a, 0xde, 0x11, 0x74, 0xbf, 0x27, 0xda, 0x66, 0x4f, 0xb4, 0xc7, 0x3d, + 0xd1, 0x2e, 0xed, 0x83, 0xcd, 0x67, 0xea, 0x31, 0xfe, 0x2e, 0x79, 0x10, 0xb3, 0x2a, 0xb0, 0xdc, + 0x66, 0x37, 0x07, 0xa9, 0xa9, 0x4b, 0xdc, 0x86, 0xca, 0xe7, 0xd7, 0x4b, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x90, 0xf4, 0xd8, 0xe7, 0x08, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/x/devgas/v1/types/msg.go b/x/devgas/v1/types/msg.go index ab2da1005..608bc8f36 100644 --- a/x/devgas/v1/types/msg.go +++ b/x/devgas/v1/types/msg.go @@ -39,7 +39,7 @@ func NewMsgRegisterFeeShare( // Route returns the name of the module func (msg MsgRegisterFeeShare) Route() string { return RouterKey } -// Type returns the the action +// Type returns the action func (msg MsgRegisterFeeShare) Type() string { return TypeMsgRegisterFeeShare } // ValidateBasic runs stateless checks on the message @@ -129,7 +129,7 @@ func NewMsgUpdateFeeShare( // Route returns the name of the module func (msg MsgUpdateFeeShare) Route() string { return RouterKey } -// Type returns the the action +// Type returns the action func (msg MsgUpdateFeeShare) Type() string { return TypeMsgUpdateFeeShare } // ValidateBasic runs stateless checks on the message diff --git a/x/devgas/v1/types/msg_test.go b/x/devgas/v1/types/msg_test.go index fb41cba0f..38ef569cf 100644 --- a/x/devgas/v1/types/msg_test.go +++ b/x/devgas/v1/types/msg_test.go @@ -276,7 +276,7 @@ func (s *MsgsTestSuite) TestQuery_ValidateBasic() { }, }, } { - s.T().Run(tc.name, func(t *testing.T) { + s.Run(tc.name, func() { tc.test() }) } diff --git a/x/devgas/v1/types/params.go b/x/devgas/v1/types/params.go index 03d154a74..532436c73 100644 --- a/x/devgas/v1/types/params.go +++ b/x/devgas/v1/types/params.go @@ -3,6 +3,7 @@ package types import ( "fmt" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -27,7 +28,7 @@ func DefaultParams() ModuleParams { } } -func validateBool(i interface{}) error { +func validateBool(i any) error { _, ok := i.(bool) if !ok { return fmt.Errorf("invalid parameter type: %T", i) @@ -36,7 +37,7 @@ func validateBool(i interface{}) error { return nil } -func validateShares(i interface{}) error { +func validateShares(i any) error { v, ok := i.(sdk.Dec) if !ok { @@ -51,14 +52,14 @@ func validateShares(i interface{}) error { return fmt.Errorf("value cannot be negative: %T", i) } - if v.GT(sdk.OneDec()) { + if v.GT(math.LegacyOneDec()) { return fmt.Errorf("value cannot be greater than 1: %T", i) } return nil } -func validateArray(i interface{}) error { +func validateArray(i any) error { _, ok := i.([]string) if !ok { return fmt.Errorf("invalid parameter type: %T", i) diff --git a/x/devgas/v1/types/params_legacy.go b/x/devgas/v1/types/params_legacy.go index 6737082a5..259708e8a 100644 --- a/x/devgas/v1/types/params_legacy.go +++ b/x/devgas/v1/types/params_legacy.go @@ -3,14 +3,14 @@ package types // TODO: Remove this and params_legacy_test.go after v0.47.x (v16) upgrade import ( - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) // Parameter store key var ( DefaultEnableFeeShare = true - DefaultDeveloperShares = sdk.NewDecWithPrec(50, 2) // 50% + DefaultDeveloperShares = math.LegacyNewDecWithPrec(50, 2) // 50% // DefaultAllowedDenoms = []string(nil) // all allowed DefaultAllowedDenoms = []string{} // all allowed diff --git a/x/devgas/v1/types/params_legacy_test.go b/x/devgas/v1/types/params_legacy_test.go index 7ff58d7d7..6a86405cf 100644 --- a/x/devgas/v1/types/params_legacy_test.go +++ b/x/devgas/v1/types/params_legacy_test.go @@ -3,9 +3,9 @@ package types import ( "testing" + "cosmossdk.io/math" "github.com/stretchr/testify/require" - sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) @@ -20,7 +20,7 @@ func TestLegacyParamSetPairs(t *testing.T) { } func TestLegacyParamsValidate(t *testing.T) { - devShares := sdk.NewDecWithPrec(60, 2) + devShares := math.LegacyNewDecWithPrec(60, 2) acceptedDenoms := []string{"unibi"} testCases := []struct { @@ -41,7 +41,7 @@ func TestLegacyParamsValidate(t *testing.T) { }, { "valid: 100% devs", - ModuleParams{true, sdk.NewDecFromInt(sdk.NewInt(1)), acceptedDenoms}, + ModuleParams{true, math.LegacyNewDecFromInt(math.NewInt(1)), acceptedDenoms}, false, }, { @@ -51,17 +51,17 @@ func TestLegacyParamsValidate(t *testing.T) { }, { "invalid: share > 1", - ModuleParams{true, sdk.NewDecFromInt(sdk.NewInt(2)), acceptedDenoms}, + ModuleParams{true, math.LegacyNewDecFromInt(math.NewInt(2)), acceptedDenoms}, true, }, { "invalid: share < 0", - ModuleParams{true, sdk.NewDecFromInt(sdk.NewInt(-1)), acceptedDenoms}, + ModuleParams{true, math.LegacyNewDecFromInt(math.NewInt(-1)), acceptedDenoms}, true, }, { "valid: all denoms allowed", - ModuleParams{true, sdk.NewDecFromInt(sdk.NewInt(-1)), []string{}}, + ModuleParams{true, math.LegacyNewDecFromInt(math.NewInt(-1)), []string{}}, true, }, } @@ -83,14 +83,14 @@ func TestLegacyParamsValidateShares(t *testing.T) { expError bool }{ {"default", DefaultDeveloperShares, false}, - {"valid", sdk.NewDecFromInt(sdk.NewInt(1)), false}, + {"valid", math.LegacyNewDecFromInt(math.NewInt(1)), false}, {"invalid - wrong type - bool", false, true}, {"invalid - wrong type - string", "", true}, {"invalid - wrong type - int64", int64(123), true}, - {"invalid - wrong type - math.Int", sdk.NewInt(1), true}, + {"invalid - wrong type - math.Int", math.NewInt(1), true}, {"invalid - is nil", nil, true}, - {"invalid - is negative", sdk.NewDecFromInt(sdk.NewInt(-1)), true}, - {"invalid - is > 1", sdk.NewDecFromInt(sdk.NewInt(2)), true}, + {"invalid - is negative", math.LegacyNewDecFromInt(math.NewInt(-1)), true}, + {"invalid - is > 1", math.LegacyNewDecFromInt(math.NewInt(2)), true}, } for _, tc := range testCases { err := validateShares(tc.value) diff --git a/x/devgas/v1/types/params_test.go b/x/devgas/v1/types/params_test.go index 3a81c3529..422eeac98 100644 --- a/x/devgas/v1/types/params_test.go +++ b/x/devgas/v1/types/params_test.go @@ -3,9 +3,8 @@ package types import ( "testing" + "cosmossdk.io/math" "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" ) // func TestParamKeyTable(t *testing.T) { @@ -19,7 +18,7 @@ func TestDefaultParams(t *testing.T) { } func TestParamsValidate(t *testing.T) { - devShares := sdk.NewDecWithPrec(60, 2) + devShares := math.LegacyNewDecWithPrec(60, 2) acceptedDenoms := []string{"unibi"} testCases := []struct { @@ -41,7 +40,7 @@ func TestParamsValidate(t *testing.T) { }, { "valid: 100% devs", - ModuleParams{true, sdk.NewDecFromInt(sdk.NewInt(1)), acceptedDenoms}, + ModuleParams{true, math.LegacyNewDecFromInt(math.NewInt(1)), acceptedDenoms}, false, }, { @@ -51,17 +50,17 @@ func TestParamsValidate(t *testing.T) { }, { "invalid: share > 1", - ModuleParams{true, sdk.NewDecFromInt(sdk.NewInt(2)), acceptedDenoms}, + ModuleParams{true, math.LegacyNewDecFromInt(math.NewInt(2)), acceptedDenoms}, true, }, { "invalid: share < 0", - ModuleParams{true, sdk.NewDecFromInt(sdk.NewInt(-1)), acceptedDenoms}, + ModuleParams{true, math.LegacyNewDecFromInt(math.NewInt(-1)), acceptedDenoms}, true, }, { "valid: all denoms allowed", - ModuleParams{true, sdk.NewDecFromInt(sdk.NewInt(-1)), []string{}}, + ModuleParams{true, math.LegacyNewDecFromInt(math.NewInt(-1)), []string{}}, true, }, } @@ -83,14 +82,14 @@ func TestParamsValidateShares(t *testing.T) { expError bool }{ {"default", DefaultDeveloperShares, false}, - {"valid", sdk.NewDecFromInt(sdk.NewInt(1)), false}, + {"valid", math.LegacyNewDecFromInt(math.NewInt(1)), false}, {"invalid - wrong type - bool", false, true}, {"invalid - wrong type - string", "", true}, {"invalid - wrong type - int64", int64(123), true}, - {"invalid - wrong type - math.Int", sdk.NewInt(1), true}, + {"invalid - wrong type - math.Int", math.NewInt(1), true}, {"invalid - is nil", nil, true}, - {"invalid - is negative", sdk.NewDecFromInt(sdk.NewInt(-1)), true}, - {"invalid - is > 1", sdk.NewDecFromInt(sdk.NewInt(2)), true}, + {"invalid - is negative", math.LegacyNewDecFromInt(math.NewInt(-1)), true}, + {"invalid - is > 1", math.LegacyNewDecFromInt(math.NewInt(2)), true}, } for _, tc := range testCases { err := validateShares(tc.value) diff --git a/x/devgas/v1/types/query.pb.go b/x/devgas/v1/types/query.pb.go index fad0f6db7..ec6ccc97c 100644 --- a/x/devgas/v1/types/query.pb.go +++ b/x/devgas/v1/types/query.pb.go @@ -406,43 +406,43 @@ func init() { func init() { proto.RegisterFile("nibiru/devgas/v1/query.proto", fileDescriptor_b68d3a02185e7c52) } var fileDescriptor_b68d3a02185e7c52 = []byte{ - // 567 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x4f, 0x8b, 0xd3, 0x5c, - 0x14, 0xc6, 0x9b, 0xf7, 0x1d, 0x4b, 0xe7, 0xba, 0x70, 0xbc, 0x76, 0xa4, 0x84, 0x31, 0xd6, 0xa0, - 0xb6, 0x23, 0x98, 0x4b, 0xa6, 0x82, 0x08, 0x6e, 0xa6, 0x82, 0xe0, 0x42, 0xd1, 0x0e, 0x2a, 0xb8, - 0x19, 0x6e, 0x9a, 0x33, 0x69, 0xa0, 0xcd, 0xcd, 0xe4, 0xa6, 0xad, 0x45, 0xba, 0xf1, 0x0b, 0x28, - 0xe8, 0xd7, 0x71, 0x3f, 0xcb, 0x01, 0x37, 0xae, 0x44, 0x5a, 0x37, 0x7e, 0x0b, 0xe9, 0xfd, 0x53, - 0x6d, 0xd3, 0x69, 0x18, 0x70, 0x97, 0x9e, 0x73, 0x9e, 0xf3, 0xfc, 0x7a, 0xf2, 0xb4, 0x68, 0x27, - 0x0a, 0xbd, 0x30, 0xe9, 0x13, 0x1f, 0x06, 0x01, 0xe5, 0x64, 0xe0, 0x92, 0xe3, 0x3e, 0x24, 0x23, - 0x27, 0x4e, 0x58, 0xca, 0xf0, 0x96, 0xec, 0x3a, 0xb2, 0xeb, 0x0c, 0x5c, 0xf3, 0x4e, 0x9b, 0xf1, - 0x1e, 0xe3, 0xc4, 0xa3, 0x1c, 0xe4, 0x28, 0x19, 0xb8, 0x1e, 0xa4, 0xd4, 0x25, 0x31, 0x0d, 0xc2, - 0x88, 0xa6, 0x21, 0x8b, 0xa4, 0xda, 0xb4, 0x32, 0xbb, 0x03, 0x88, 0x80, 0x87, 0x5c, 0xf5, 0xaf, - 0x65, 0xfa, 0xca, 0x47, 0xb6, 0xcb, 0x01, 0x0b, 0x98, 0x78, 0x24, 0xb3, 0x27, 0x55, 0xdd, 0x09, - 0x18, 0x0b, 0xba, 0x40, 0x68, 0x1c, 0x12, 0x1a, 0x45, 0x2c, 0x15, 0x8e, 0x4a, 0x63, 0x37, 0xd0, - 0xf6, 0x8b, 0x19, 0xd4, 0x63, 0x80, 0x83, 0x0e, 0x4d, 0x80, 0xb7, 0xe0, 0xb8, 0x0f, 0x3c, 0xc5, - 0x26, 0x2a, 0xf9, 0x10, 0x77, 0xd9, 0x08, 0x92, 0x8a, 0x51, 0x35, 0xea, 0x9b, 0xad, 0xf9, 0x67, - 0xfb, 0x15, 0xba, 0xba, 0x2c, 0xe2, 0x31, 0x8b, 0x38, 0xe0, 0x87, 0xa8, 0x74, 0x04, 0xc0, 0x67, - 0xc5, 0x8a, 0x51, 0xfd, 0xbf, 0x7e, 0x71, 0xcf, 0x74, 0x96, 0x4f, 0xe2, 0x68, 0x59, 0x73, 0xe3, - 0xe4, 0xfb, 0xf5, 0x42, 0x6b, 0xae, 0xb0, 0xf7, 0x51, 0x79, 0x61, 0xaf, 0x66, 0xd9, 0x45, 0x5b, - 0x6d, 0x16, 0xa5, 0x09, 0x6d, 0xa7, 0x87, 0xd4, 0xf7, 0x13, 0xe0, 0x5c, 0x31, 0x5d, 0xd2, 0xf5, - 0x7d, 0x59, 0xb6, 0x5f, 0x2e, 0x7d, 0x9f, 0x33, 0xc8, 0x8c, 0x73, 0x92, 0x95, 0x11, 0x16, 0x6b, - 0x9f, 0xd3, 0x84, 0xf6, 0xf4, 0x8d, 0xec, 0x03, 0x74, 0x65, 0xa1, 0x3a, 0xb7, 0x2a, 0xc6, 0xa2, - 0xa2, 0x8c, 0xac, 0xac, 0xd1, 0x53, 0xe6, 0xf7, 0xbb, 0x20, 0x75, 0xca, 0x4c, 0x69, 0xec, 0x16, - 0xba, 0xb1, 0x78, 0xdc, 0xe6, 0xe8, 0x75, 0x98, 0x76, 0xfc, 0x84, 0x0e, 0x21, 0xd1, 0x17, 0xb9, - 0x8b, 0xf0, 0x70, 0x5e, 0x5c, 0xba, 0xc9, 0xe5, 0x3f, 0x1d, 0x7d, 0x15, 0x0f, 0xd9, 0xeb, 0x76, - 0xfe, 0x8b, 0x97, 0xb7, 0xf7, 0x6b, 0x03, 0x5d, 0x10, 0x26, 0xf8, 0x83, 0x81, 0x36, 0xe7, 0x4e, - 0xb8, 0x96, 0xdd, 0xb1, 0x32, 0x71, 0x66, 0x3d, 0x7f, 0x50, 0x82, 0xda, 0xe4, 0xfd, 0xd7, 0x9f, - 0x9f, 0xfe, 0xdb, 0xc5, 0x35, 0x92, 0xf9, 0x41, 0x1c, 0x01, 0x1c, 0x0a, 0x1e, 0x4e, 0xde, 0xe9, - 0xbc, 0x8e, 0xf1, 0x67, 0x03, 0x95, 0xf4, 0x1a, 0x7c, 0x3b, 0xc7, 0x47, 0xf3, 0xd4, 0x72, 0xe7, - 0x14, 0xce, 0x7d, 0x81, 0xe3, 0x62, 0xb2, 0x1e, 0x67, 0x39, 0xc2, 0x63, 0x3c, 0x44, 0x45, 0x19, - 0x01, 0x7c, 0xf3, 0x0c, 0xaf, 0x85, 0xbc, 0x99, 0xb7, 0x72, 0xa6, 0x14, 0x4f, 0x55, 0xf0, 0x98, - 0xb8, 0x92, 0xe5, 0x91, 0x19, 0xc3, 0x5f, 0x0c, 0xb4, 0xbd, 0x32, 0x0b, 0xb8, 0x91, 0xf7, 0x12, - 0x56, 0xa4, 0xd1, 0xbc, 0x77, 0x3e, 0x91, 0xc2, 0x7c, 0x20, 0x30, 0x1b, 0xd8, 0x5d, 0x7f, 0xb6, - 0x6c, 0xce, 0xc7, 0xcd, 0x27, 0x27, 0x13, 0xcb, 0x38, 0x9d, 0x58, 0xc6, 0x8f, 0x89, 0x65, 0x7c, - 0x9c, 0x5a, 0x85, 0xd3, 0xa9, 0x55, 0xf8, 0x36, 0xb5, 0x0a, 0x6f, 0x48, 0x10, 0xa6, 0x9d, 0xbe, - 0xe7, 0xb4, 0x59, 0x8f, 0x3c, 0x13, 0x6b, 0x1f, 0x75, 0x68, 0x18, 0x69, 0x8b, 0xb7, 0x7f, 0x99, - 0xa4, 0xa3, 0x18, 0xb8, 0x57, 0x14, 0xff, 0x83, 0x8d, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x72, - 0x1e, 0xa0, 0x89, 0xd8, 0x05, 0x00, 0x00, + // 568 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xcf, 0x8e, 0xd2, 0x50, + 0x14, 0xc6, 0xa9, 0x8e, 0x84, 0xb9, 0x2e, 0x1c, 0xaf, 0x8c, 0x21, 0xcd, 0x58, 0xb1, 0x51, 0x61, + 0x4c, 0xec, 0x4d, 0xc1, 0xc4, 0x98, 0xb8, 0x19, 0x4c, 0x5c, 0xa9, 0x51, 0x26, 0x6a, 0xe2, 0x66, + 0x72, 0x4b, 0xcf, 0x94, 0x26, 0xd0, 0xdb, 0xe9, 0x2d, 0x20, 0x31, 0x6c, 0x7c, 0x01, 0x4d, 0xf4, + 0x75, 0xdc, 0xcf, 0x72, 0x12, 0x37, 0xae, 0x8c, 0x01, 0x37, 0xbe, 0x85, 0xe1, 0xfe, 0x41, 0xa1, + 0x0c, 0x64, 0x92, 0xd9, 0x95, 0x73, 0xce, 0x77, 0xbe, 0x1f, 0xa7, 0x1f, 0xa0, 0x9d, 0x28, 0xf4, + 0xc2, 0xa4, 0x47, 0x7c, 0xe8, 0x07, 0x94, 0x93, 0xbe, 0x4b, 0x8e, 0x7a, 0x90, 0x0c, 0x9d, 0x38, + 0x61, 0x29, 0xc3, 0x5b, 0xb2, 0xeb, 0xc8, 0xae, 0xd3, 0x77, 0xcd, 0x7b, 0x2d, 0xc6, 0xbb, 0x8c, + 0x13, 0x8f, 0x72, 0x90, 0xa3, 0xa4, 0xef, 0x7a, 0x90, 0x52, 0x97, 0xc4, 0x34, 0x08, 0x23, 0x9a, + 0x86, 0x2c, 0x92, 0x6a, 0xd3, 0xca, 0xec, 0x0e, 0x20, 0x02, 0x1e, 0x72, 0xd5, 0xbf, 0x91, 0xe9, + 0x2b, 0x1f, 0xd9, 0x2e, 0x06, 0x2c, 0x60, 0xe2, 0x91, 0x4c, 0x9f, 0x54, 0x75, 0x27, 0x60, 0x2c, + 0xe8, 0x00, 0xa1, 0x71, 0x48, 0x68, 0x14, 0xb1, 0x54, 0x38, 0x2a, 0x8d, 0x5d, 0x47, 0xdb, 0xaf, + 0xa6, 0x50, 0x4f, 0x01, 0xf6, 0xdb, 0x34, 0x01, 0xde, 0x84, 0xa3, 0x1e, 0xf0, 0x14, 0x9b, 0xa8, + 0xe0, 0x43, 0xdc, 0x61, 0x43, 0x48, 0x4a, 0x46, 0xd9, 0xa8, 0x6e, 0x36, 0x67, 0x9f, 0xed, 0x37, + 0xe8, 0xfa, 0xa2, 0x88, 0xc7, 0x2c, 0xe2, 0x80, 0x1f, 0xa3, 0xc2, 0x21, 0x00, 0x9f, 0x16, 0x4b, + 0x46, 0xf9, 0x62, 0xf5, 0x72, 0xcd, 0x74, 0x16, 0x4f, 0xe2, 0x68, 0x59, 0x63, 0xe3, 0xf8, 0xe7, + 0xcd, 0x5c, 0x73, 0xa6, 0xb0, 0xf7, 0x50, 0x71, 0x6e, 0xaf, 0x66, 0xd9, 0x45, 0x5b, 0x2d, 0x16, + 0xa5, 0x09, 0x6d, 0xa5, 0x07, 0xd4, 0xf7, 0x13, 0xe0, 0x5c, 0x31, 0x5d, 0xd1, 0xf5, 0x3d, 0x59, + 0xb6, 0x5f, 0x2f, 0x7c, 0x9f, 0x53, 0xc8, 0x8c, 0x33, 0x92, 0x15, 0x11, 0x16, 0x6b, 0x5f, 0xd2, + 0x84, 0x76, 0xf5, 0x8d, 0xec, 0x7d, 0x74, 0x6d, 0xae, 0x3a, 0xb3, 0xca, 0xc7, 0xa2, 0xa2, 0x8c, + 0xac, 0xac, 0xd1, 0x73, 0xe6, 0xf7, 0x3a, 0x20, 0x75, 0xca, 0x4c, 0x69, 0xec, 0x26, 0xba, 0x35, + 0x7f, 0xdc, 0xc6, 0xf0, 0x6d, 0x98, 0xb6, 0xfd, 0x84, 0x0e, 0x20, 0xd1, 0x17, 0xb9, 0x8f, 0xf0, + 0x60, 0x56, 0x5c, 0xb8, 0xc9, 0xd5, 0x7f, 0x1d, 0x7d, 0x15, 0x0f, 0xd9, 0xab, 0x76, 0x9e, 0xc7, + 0xcb, 0xab, 0xfd, 0xd9, 0x40, 0x97, 0x84, 0x09, 0xfe, 0x64, 0xa0, 0xcd, 0x99, 0x13, 0xae, 0x64, + 0x77, 0x2c, 0x4d, 0x9c, 0x59, 0x5d, 0x3f, 0x28, 0x41, 0x6d, 0xf2, 0xf1, 0xfb, 0xef, 0x2f, 0x17, + 0x76, 0x71, 0x85, 0x64, 0x7e, 0x10, 0x87, 0x00, 0x07, 0x82, 0x87, 0x93, 0x0f, 0x3a, 0xaf, 0x23, + 0xfc, 0xd5, 0x40, 0x05, 0xbd, 0x06, 0xdf, 0x5d, 0xe3, 0xa3, 0x79, 0x2a, 0x6b, 0xe7, 0x14, 0xce, + 0x43, 0x81, 0xe3, 0x62, 0xb2, 0x1a, 0x67, 0x31, 0xc2, 0x23, 0x3c, 0x40, 0x79, 0x19, 0x01, 0x7c, + 0xfb, 0x14, 0xaf, 0xb9, 0xbc, 0x99, 0x77, 0xd6, 0x4c, 0x29, 0x9e, 0xb2, 0xe0, 0x31, 0x71, 0x29, + 0xcb, 0x23, 0x33, 0x86, 0xbf, 0x19, 0x68, 0x7b, 0x69, 0x16, 0x70, 0x7d, 0xdd, 0x4b, 0x58, 0x92, + 0x46, 0xf3, 0xc1, 0xd9, 0x44, 0x0a, 0xf3, 0x91, 0xc0, 0xac, 0x63, 0x77, 0xf5, 0xd9, 0xb2, 0x39, + 0x1f, 0x35, 0x9e, 0x1d, 0x8f, 0x2d, 0xe3, 0x64, 0x6c, 0x19, 0xbf, 0xc6, 0x96, 0xf1, 0x79, 0x62, + 0xe5, 0x4e, 0x26, 0x56, 0xee, 0xc7, 0xc4, 0xca, 0xbd, 0xab, 0x05, 0x61, 0xda, 0xee, 0x79, 0x4e, + 0x8b, 0x75, 0xc9, 0x0b, 0xb1, 0xf6, 0x49, 0x9b, 0x86, 0x91, 0xb6, 0xe8, 0xd7, 0xc8, 0xfb, 0xff, + 0x7c, 0xd2, 0x61, 0x0c, 0xdc, 0xcb, 0x8b, 0xbf, 0xc2, 0xfa, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xa5, 0xe5, 0x49, 0x1c, 0xdb, 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/devgas/v1/types/tx.pb.go b/x/devgas/v1/types/tx.pb.go index a034bea94..90fd5d17f 100644 --- a/x/devgas/v1/types/tx.pb.go +++ b/x/devgas/v1/types/tx.pb.go @@ -445,43 +445,43 @@ func init() { func init() { proto.RegisterFile("nibiru/devgas/v1/tx.proto", fileDescriptor_72949c99a02cd615) } var fileDescriptor_72949c99a02cd615 = []byte{ - // 565 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0x41, 0x6b, 0x13, 0x4d, - 0x18, 0xc7, 0xb3, 0x6d, 0x28, 0x74, 0xde, 0x97, 0x36, 0x5d, 0x0b, 0x6d, 0x52, 0xdd, 0xea, 0xaa, - 0xa5, 0x55, 0xb3, 0x43, 0x2b, 0x78, 0x28, 0x5e, 0x4c, 0x41, 0xf0, 0x10, 0x91, 0x14, 0x2f, 0x22, - 0x84, 0xc9, 0xee, 0x30, 0x19, 0x48, 0x76, 0x96, 0x99, 0xd9, 0xb4, 0xb9, 0xf6, 0x13, 0x14, 0x3c, - 0xe8, 0x49, 0x7a, 0xf0, 0x03, 0x78, 0xf0, 0x43, 0xf4, 0x58, 0xf4, 0xe2, 0x49, 0x24, 0x11, 0xf4, - 0x63, 0x48, 0x66, 0x66, 0x37, 0xdd, 0x64, 0x5b, 0x7b, 0x11, 0xbc, 0x25, 0xf3, 0xff, 0x3d, 0xcf, - 0xf3, 0xdb, 0xe5, 0x99, 0x05, 0xe5, 0x90, 0xb6, 0x28, 0x8f, 0x61, 0x80, 0x7b, 0x04, 0x09, 0xd8, - 0xdb, 0x86, 0xf2, 0xd0, 0x8b, 0x38, 0x93, 0xcc, 0x2e, 0xe9, 0xc8, 0xd3, 0x91, 0xd7, 0xdb, 0xae, - 0x2c, 0x13, 0x46, 0x98, 0x0a, 0xe1, 0xe8, 0x97, 0xe6, 0x2a, 0xd7, 0x09, 0x63, 0xa4, 0x83, 0x21, - 0x8a, 0x28, 0x44, 0x61, 0xc8, 0x24, 0x92, 0x94, 0x85, 0xc2, 0xa4, 0x2b, 0x3e, 0x13, 0x5d, 0x26, - 0x60, 0x57, 0x90, 0x51, 0xf7, 0xae, 0x20, 0x26, 0x28, 0xeb, 0xa0, 0xa9, 0xfb, 0xe9, 0x3f, 0x26, - 0x72, 0xa6, 0xa4, 0x08, 0x0e, 0xb1, 0xa0, 0x26, 0x77, 0x4f, 0x2c, 0x70, 0xad, 0x2e, 0x48, 0x03, - 0x13, 0x2a, 0x24, 0xe6, 0x4f, 0x31, 0xde, 0x6f, 0x23, 0x8e, 0xed, 0x2d, 0x50, 0xf2, 0x59, 0x28, - 0x39, 0xf2, 0x65, 0x13, 0x05, 0x01, 0xc7, 0x42, 0xac, 0x5a, 0x37, 0xad, 0xcd, 0xf9, 0xc6, 0x62, - 0x72, 0xfe, 0x44, 0x1f, 0x8f, 0xd0, 0x00, 0x47, 0x1d, 0xd6, 0xc7, 0x3c, 0x45, 0x67, 0x34, 0x9a, - 0x9c, 0x27, 0x68, 0x15, 0xd8, 0x07, 0x54, 0xb6, 0x03, 0x8e, 0x0e, 0xce, 0xc1, 0xb3, 0x0a, 0x5e, - 0x1a, 0x27, 0x06, 0xdf, 0x2d, 0xfe, 0x3a, 0x59, 0x2f, 0xb8, 0x37, 0xc0, 0x5a, 0x8e, 0x61, 0x03, - 0x8b, 0x88, 0x85, 0x02, 0xbb, 0xef, 0x2d, 0xb0, 0x54, 0x17, 0xe4, 0x65, 0x14, 0x20, 0x89, 0xff, - 0x45, 0xff, 0x35, 0x50, 0x9e, 0xf2, 0x4b, 0xed, 0x99, 0x92, 0xdf, 0x43, 0xa1, 0x8f, 0x3b, 0x7f, - 0x57, 0x3e, 0x63, 0x93, 0x1d, 0x98, 0xda, 0xbc, 0xb5, 0xc0, 0x62, 0xea, 0xfa, 0x02, 0x71, 0xd4, - 0x15, 0xf6, 0x23, 0x30, 0x8f, 0x62, 0xd9, 0x66, 0x9c, 0xca, 0xbe, 0xb6, 0xa8, 0xad, 0x7e, 0xfe, - 0x54, 0x5d, 0x36, 0x6b, 0x66, 0xba, 0xef, 0x4b, 0x4e, 0x43, 0xd2, 0x18, 0xa3, 0xf6, 0x63, 0x30, - 0x17, 0xa9, 0x0e, 0xca, 0xe7, 0xbf, 0x1d, 0xc7, 0x9b, 0xbc, 0x04, 0x5e, 0x9d, 0x05, 0x71, 0xc7, - 0xcc, 0xa9, 0x15, 0x4f, 0xbf, 0xad, 0x17, 0x1a, 0xa6, 0x66, 0x77, 0xe1, 0xe8, 0xe7, 0xc7, 0x7b, - 0xe3, 0x6e, 0x6e, 0x19, 0xac, 0x4c, 0x88, 0x25, 0xd2, 0x3b, 0x1f, 0x8a, 0x60, 0xb6, 0x2e, 0x88, - 0xfd, 0xce, 0x02, 0xa5, 0xa9, 0x3d, 0xbe, 0x9b, 0x33, 0x75, 0x7a, 0x99, 0x2a, 0xd5, 0x2b, 0x61, - 0xe9, 0x7b, 0xf2, 0x8e, 0xbe, 0xfc, 0x78, 0x33, 0xb3, 0xe9, 0x6e, 0xc0, 0x9c, 0x3b, 0x0f, 0xb9, - 0x29, 0x6b, 0xa6, 0x16, 0xc7, 0x16, 0x58, 0x98, 0x58, 0xd0, 0xdb, 0xb9, 0x13, 0xb3, 0x50, 0xe5, - 0xfe, 0x15, 0xa0, 0x54, 0xea, 0x81, 0x92, 0xda, 0x70, 0xef, 0xe4, 0x4a, 0xc5, 0xaa, 0x28, 0xab, - 0x34, 0xb1, 0x76, 0xf9, 0x4a, 0x59, 0xe8, 0x02, 0xa5, 0x0b, 0xf6, 0xe9, 0x72, 0x25, 0x5f, 0x15, - 0x8d, 0x95, 0x5e, 0x83, 0xff, 0x33, 0x9b, 0x77, 0xeb, 0x92, 0xa7, 0xd7, 0x48, 0x65, 0xeb, 0x8f, - 0x48, 0xe2, 0x52, 0x7b, 0x76, 0x3a, 0x70, 0xac, 0xb3, 0x81, 0x63, 0x7d, 0x1f, 0x38, 0xd6, 0xf1, - 0xd0, 0x29, 0x9c, 0x0d, 0x9d, 0xc2, 0xd7, 0xa1, 0x53, 0x78, 0x05, 0x09, 0x95, 0xed, 0xb8, 0xe5, - 0xf9, 0xac, 0x0b, 0x9f, 0xab, 0x76, 0x7b, 0x6d, 0x44, 0xc3, 0xc4, 0xf9, 0xf0, 0xbc, 0x75, 0x3f, - 0xc2, 0xa2, 0x35, 0xa7, 0xbe, 0x9d, 0x0f, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x24, 0xc1, - 0x45, 0xf2, 0x05, 0x00, 0x00, + // 566 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0xc1, 0x6a, 0x13, 0x41, + 0x18, 0xc7, 0xb3, 0x6d, 0x28, 0x74, 0x94, 0x36, 0x5d, 0x0b, 0x6d, 0x52, 0xdd, 0xea, 0xaa, 0xa5, + 0x55, 0xb3, 0x43, 0x23, 0x78, 0x28, 0x5e, 0x4c, 0xc1, 0x93, 0x11, 0x49, 0xf1, 0x22, 0x42, 0x98, + 0xec, 0x0e, 0x93, 0x81, 0x64, 0x67, 0x99, 0x99, 0xa4, 0xcd, 0xb5, 0x4f, 0x50, 0xf0, 0xa0, 0x27, + 0xe9, 0xc1, 0x07, 0xf0, 0xe0, 0x43, 0xf4, 0x58, 0xf4, 0xe2, 0x49, 0x24, 0x11, 0xf4, 0x31, 0x24, + 0x33, 0xb3, 0x9b, 0x6e, 0xb2, 0xad, 0xbd, 0x08, 0xbd, 0x25, 0xf3, 0xff, 0x7d, 0xdf, 0xf7, 0xdb, + 0xe5, 0x9b, 0x05, 0xc5, 0x90, 0x36, 0x29, 0xef, 0xc2, 0x00, 0xf7, 0x08, 0x12, 0xb0, 0xb7, 0x0d, + 0xe5, 0x81, 0x17, 0x71, 0x26, 0x99, 0x5d, 0xd0, 0x91, 0xa7, 0x23, 0xaf, 0xb7, 0x5d, 0x5a, 0x26, + 0x8c, 0x30, 0x15, 0xc2, 0xd1, 0x2f, 0xcd, 0x95, 0x6e, 0x12, 0xc6, 0x48, 0x1b, 0x43, 0x14, 0x51, + 0x88, 0xc2, 0x90, 0x49, 0x24, 0x29, 0x0b, 0x85, 0x49, 0x57, 0x7c, 0x26, 0x3a, 0x4c, 0xc0, 0x8e, + 0x20, 0xa3, 0xee, 0x1d, 0x41, 0x4c, 0x50, 0xd4, 0x41, 0x43, 0xf7, 0xd3, 0x7f, 0x4c, 0xe4, 0x4c, + 0x49, 0x11, 0x1c, 0x62, 0x41, 0x4d, 0xee, 0x1e, 0x5b, 0xe0, 0x46, 0x4d, 0x90, 0x3a, 0x26, 0x54, + 0x48, 0xcc, 0x9f, 0x63, 0xbc, 0xd7, 0x42, 0x1c, 0xdb, 0x5b, 0xa0, 0xe0, 0xb3, 0x50, 0x72, 0xe4, + 0xcb, 0x06, 0x0a, 0x02, 0x8e, 0x85, 0x58, 0xb5, 0x6e, 0x5b, 0x9b, 0xf3, 0xf5, 0xc5, 0xf8, 0xfc, + 0x99, 0x3e, 0x1e, 0xa1, 0x01, 0x8e, 0xda, 0xac, 0x8f, 0x79, 0x82, 0xce, 0x68, 0x34, 0x3e, 0x8f, + 0xd1, 0x32, 0xb0, 0xf7, 0xa9, 0x6c, 0x05, 0x1c, 0xed, 0x9f, 0x81, 0x67, 0x15, 0xbc, 0x34, 0x4e, + 0x0c, 0xbe, 0x93, 0xff, 0x73, 0xbc, 0x9e, 0x73, 0x6f, 0x81, 0xb5, 0x0c, 0xc3, 0x3a, 0x16, 0x11, + 0x0b, 0x05, 0x76, 0x3f, 0x5a, 0x60, 0xa9, 0x26, 0xc8, 0xeb, 0x28, 0x40, 0x12, 0x5f, 0x45, 0xff, + 0x35, 0x50, 0x9c, 0xf2, 0x4b, 0xec, 0x99, 0x92, 0xdf, 0x45, 0xa1, 0x8f, 0xdb, 0xff, 0x57, 0x3e, + 0x65, 0x93, 0x1e, 0x98, 0xd8, 0xbc, 0xb7, 0xc0, 0x62, 0xe2, 0xfa, 0x0a, 0x71, 0xd4, 0x11, 0xf6, + 0x13, 0x30, 0x8f, 0xba, 0xb2, 0xc5, 0x38, 0x95, 0x7d, 0x6d, 0x51, 0x5d, 0xfd, 0xfa, 0xa5, 0xbc, + 0x6c, 0xd6, 0xcc, 0x74, 0xdf, 0x93, 0x9c, 0x86, 0xa4, 0x3e, 0x46, 0xed, 0xa7, 0x60, 0x2e, 0x52, + 0x1d, 0x94, 0xcf, 0xb5, 0x8a, 0xe3, 0x4d, 0x5e, 0x02, 0xaf, 0xc6, 0x82, 0x6e, 0xdb, 0xcc, 0xa9, + 0xe6, 0x4f, 0x7e, 0xac, 0xe7, 0xea, 0xa6, 0x66, 0x67, 0xe1, 0xf0, 0xf7, 0xe7, 0x07, 0xe3, 0x6e, + 0x6e, 0x11, 0xac, 0x4c, 0x88, 0xc5, 0xd2, 0x95, 0x4f, 0x79, 0x30, 0x5b, 0x13, 0xc4, 0xfe, 0x60, + 0x81, 0xc2, 0xd4, 0x1e, 0xdf, 0xcf, 0x98, 0x3a, 0xbd, 0x4c, 0xa5, 0xf2, 0xa5, 0xb0, 0xe4, 0x3d, + 0x79, 0x87, 0xdf, 0x7e, 0xbd, 0x9b, 0xd9, 0x74, 0x37, 0x60, 0xc6, 0x9d, 0x87, 0xdc, 0x94, 0x35, + 0x12, 0x8b, 0x23, 0x0b, 0x2c, 0x4c, 0x2c, 0xe8, 0xdd, 0xcc, 0x89, 0x69, 0xa8, 0xf4, 0xf0, 0x12, + 0x50, 0x22, 0xf5, 0x48, 0x49, 0x6d, 0xb8, 0xf7, 0x32, 0xa5, 0xba, 0xaa, 0x28, 0xad, 0x34, 0xb1, + 0x76, 0xd9, 0x4a, 0x69, 0xe8, 0x1c, 0xa5, 0x73, 0xf6, 0xe9, 0x62, 0x25, 0x5f, 0x15, 0x8d, 0x95, + 0xde, 0x82, 0xeb, 0xa9, 0xcd, 0xbb, 0x73, 0xc1, 0xd3, 0x6b, 0xa4, 0xb4, 0xf5, 0x4f, 0x24, 0x76, + 0xa9, 0xbe, 0x38, 0x19, 0x38, 0xd6, 0xe9, 0xc0, 0xb1, 0x7e, 0x0e, 0x1c, 0xeb, 0x68, 0xe8, 0xe4, + 0x4e, 0x87, 0x4e, 0xee, 0xfb, 0xd0, 0xc9, 0xbd, 0xa9, 0x10, 0x2a, 0x5b, 0xdd, 0xa6, 0xe7, 0xb3, + 0x0e, 0x7c, 0xa9, 0xda, 0xed, 0xb6, 0x10, 0x0d, 0x63, 0xe7, 0x5e, 0x05, 0x1e, 0x9c, 0x15, 0xef, + 0x47, 0x58, 0x34, 0xe7, 0xd4, 0xe7, 0xf3, 0xf1, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x64, 0x69, + 0x77, 0x65, 0xf5, 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/epochs/abci.go b/x/epochs/abci.go index 319999944..b318a2254 100644 --- a/x/epochs/abci.go +++ b/x/epochs/abci.go @@ -6,8 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/epochs/keeper" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/keeper" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) // BeginBlocker of epochs module. diff --git a/x/epochs/abci_test.go b/x/epochs/abci_test.go index 483e56ee4..1b667a4ae 100644 --- a/x/epochs/abci_test.go +++ b/x/epochs/abci_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/epochs" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/epochs" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) func TestEpochInfoChangesBeginBlockerAndInitGenesis(t *testing.T) { diff --git a/x/epochs/client/cli/query.go b/x/epochs/client/cli/query.go index 4cf75f50e..2dd951ab7 100644 --- a/x/epochs/client/cli/query.go +++ b/x/epochs/client/cli/query.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) // GetQueryCmd returns the cli query commands for this module. diff --git a/x/epochs/client/cli/tx.go b/x/epochs/client/cli/tx.go index c00148ecc..7372d4c63 100644 --- a/x/epochs/client/cli/tx.go +++ b/x/epochs/client/cli/tx.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" // "github.com/cosmos/cosmos-sdk/client/flags". - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) // GetTxCmd returns the transaction commands for this module. diff --git a/x/epochs/genesis.go b/x/epochs/genesis.go index bffe0dfa7..5d1dc42ca 100644 --- a/x/epochs/genesis.go +++ b/x/epochs/genesis.go @@ -3,8 +3,8 @@ package epochs import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/epochs/keeper" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/keeper" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) // InitGenesis sets epoch info from genesis diff --git a/x/epochs/genesis_test.go b/x/epochs/genesis_test.go index 2fc851929..f861f3ea7 100644 --- a/x/epochs/genesis_test.go +++ b/x/epochs/genesis_test.go @@ -5,14 +5,13 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/genesis" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/epochs" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/epochs" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) func TestEpochsExportGenesis(t *testing.T) { @@ -21,7 +20,7 @@ func TestEpochsExportGenesis(t *testing.T) { encCfg := app.MakeEncodingConfig() appGenesis := genesis.NewTestGenesisState(encCfg) - appGenesis[types.ModuleName] = encCfg.Marshaler.MustMarshalJSON(moduleGenesisIn) + appGenesis[types.ModuleName] = encCfg.Codec.MustMarshalJSON(moduleGenesisIn) app := testapp.NewNibiruTestApp(appGenesis) ctx := testapp.NewContext(app).WithBlockTime(chainStartTime) @@ -30,37 +29,37 @@ func TestEpochsExportGenesis(t *testing.T) { require.Len(t, genesis.Epochs, 4) errMsg := fmt.Sprintf("app.EpochsKeeper.AllEpochInfos(ctx): %v\n", app.EpochsKeeper.AllEpochInfos(ctx)) - assert.EqualValues(t, genesis.Epochs[0].Identifier, "30 min") - assert.EqualValues(t, genesis.Epochs[0].StartTime, chainStartTime, errMsg) - assert.EqualValues(t, genesis.Epochs[0].Duration, time.Minute*30, errMsg) - assert.EqualValues(t, genesis.Epochs[0].CurrentEpoch, uint64(0)) - assert.EqualValues(t, genesis.Epochs[0].CurrentEpochStartHeight, int64(0)) - assert.EqualValues(t, genesis.Epochs[0].CurrentEpochStartTime, chainStartTime) - assert.EqualValues(t, genesis.Epochs[0].EpochCountingStarted, false) - - assert.EqualValues(t, genesis.Epochs[1].Identifier, "day") - assert.EqualValues(t, genesis.Epochs[1].StartTime, chainStartTime, errMsg) - assert.EqualValues(t, genesis.Epochs[1].Duration, time.Hour*24) - assert.EqualValues(t, genesis.Epochs[1].CurrentEpoch, uint64(0)) - assert.EqualValues(t, genesis.Epochs[1].CurrentEpochStartHeight, int64(0)) - assert.EqualValues(t, genesis.Epochs[1].CurrentEpochStartTime, chainStartTime, errMsg) - assert.EqualValues(t, genesis.Epochs[1].EpochCountingStarted, false) - - assert.EqualValues(t, genesis.Epochs[2].Identifier, "month") - assert.EqualValues(t, genesis.Epochs[2].StartTime, chainStartTime, errMsg) - assert.EqualValues(t, genesis.Epochs[2].Duration, time.Hour*24*30) - assert.EqualValues(t, genesis.Epochs[2].CurrentEpoch, uint64(0)) - assert.EqualValues(t, genesis.Epochs[2].CurrentEpochStartHeight, int64(0)) - assert.EqualValues(t, genesis.Epochs[2].CurrentEpochStartTime, chainStartTime, errMsg) - assert.EqualValues(t, genesis.Epochs[2].EpochCountingStarted, false) - - assert.EqualValues(t, genesis.Epochs[3].Identifier, "week") - assert.EqualValues(t, genesis.Epochs[3].StartTime, chainStartTime, errMsg) - assert.EqualValues(t, genesis.Epochs[3].Duration, time.Hour*24*7) - assert.EqualValues(t, genesis.Epochs[3].CurrentEpoch, uint64(0)) - assert.EqualValues(t, genesis.Epochs[3].CurrentEpochStartHeight, int64(0)) - assert.EqualValues(t, genesis.Epochs[3].CurrentEpochStartTime, chainStartTime, errMsg) - assert.EqualValues(t, genesis.Epochs[3].EpochCountingStarted, false) + require.Equal(t, genesis.Epochs[0].Identifier, "30 min") + require.Equal(t, genesis.Epochs[0].StartTime, chainStartTime, errMsg) + require.Equal(t, genesis.Epochs[0].Duration, time.Minute*30, errMsg) + require.Equal(t, genesis.Epochs[0].CurrentEpoch, uint64(0)) + require.Equal(t, genesis.Epochs[0].CurrentEpochStartHeight, int64(0)) + require.Equal(t, genesis.Epochs[0].CurrentEpochStartTime, chainStartTime) + require.Equal(t, genesis.Epochs[0].EpochCountingStarted, false) + + require.Equal(t, genesis.Epochs[1].Identifier, "day") + require.Equal(t, genesis.Epochs[1].StartTime, chainStartTime, errMsg) + require.Equal(t, genesis.Epochs[1].Duration, time.Hour*24) + require.Equal(t, genesis.Epochs[1].CurrentEpoch, uint64(0)) + require.Equal(t, genesis.Epochs[1].CurrentEpochStartHeight, int64(0)) + require.Equal(t, genesis.Epochs[1].CurrentEpochStartTime, chainStartTime, errMsg) + require.Equal(t, genesis.Epochs[1].EpochCountingStarted, false) + + require.Equal(t, genesis.Epochs[2].Identifier, "month") + require.Equal(t, genesis.Epochs[2].StartTime, chainStartTime, errMsg) + require.Equal(t, genesis.Epochs[2].Duration, time.Hour*24*30) + require.Equal(t, genesis.Epochs[2].CurrentEpoch, uint64(0)) + require.Equal(t, genesis.Epochs[2].CurrentEpochStartHeight, int64(0)) + require.Equal(t, genesis.Epochs[2].CurrentEpochStartTime, chainStartTime, errMsg) + require.Equal(t, genesis.Epochs[2].EpochCountingStarted, false) + + require.Equal(t, genesis.Epochs[3].Identifier, "week") + require.Equal(t, genesis.Epochs[3].StartTime, chainStartTime, errMsg) + require.Equal(t, genesis.Epochs[3].Duration, time.Hour*24*7) + require.Equal(t, genesis.Epochs[3].CurrentEpoch, uint64(0)) + require.Equal(t, genesis.Epochs[3].CurrentEpochStartHeight, int64(0)) + require.Equal(t, genesis.Epochs[3].CurrentEpochStartTime, chainStartTime, errMsg) + require.Equal(t, genesis.Epochs[3].EpochCountingStarted, false) } func TestEpochsInitGenesis(t *testing.T) { @@ -103,7 +102,7 @@ func TestEpochsInitGenesis(t *testing.T) { err := epochs.InitGenesis(ctx, app.EpochsKeeper, genesisState) require.Error(t, err) - assert.EqualError(t, genesisState.Validate(), "epoch identifier should be unique") + require.EqualError(t, genesisState.Validate(), "epoch identifier should be unique") genesisState = types.GenesisState{ Epochs: []types.EpochInfo{ @@ -123,11 +122,11 @@ func TestEpochsInitGenesis(t *testing.T) { require.NoError(t, err) epochInfo, err := app.EpochsKeeper.GetEpochInfo(ctx, "monthly") require.NoError(t, err) - assert.EqualValues(t, epochInfo.Identifier, "monthly") - assert.EqualValues(t, epochInfo.StartTime.UTC().String(), now.UTC().String()) - assert.EqualValues(t, epochInfo.Duration, time.Hour*24) - assert.EqualValues(t, epochInfo.CurrentEpoch, uint64(0)) - assert.EqualValues(t, epochInfo.CurrentEpochStartHeight, ctx.BlockHeight()) - assert.EqualValues(t, epochInfo.CurrentEpochStartTime.UTC().String(), time.Time{}.String()) - assert.EqualValues(t, epochInfo.EpochCountingStarted, true) + require.Equal(t, epochInfo.Identifier, "monthly") + require.Equal(t, epochInfo.StartTime.UTC().String(), now.UTC().String()) + require.Equal(t, epochInfo.Duration, time.Hour*24) + require.Equal(t, epochInfo.CurrentEpoch, uint64(0)) + require.Equal(t, epochInfo.CurrentEpochStartHeight, ctx.BlockHeight()) + require.Equal(t, epochInfo.CurrentEpochStartTime.UTC().String(), time.Time{}.String()) + require.Equal(t, epochInfo.EpochCountingStarted, true) } diff --git a/x/epochs/integration/action/epoch.go b/x/epochs/integration/action/epoch.go index 77173a469..2e7058e00 100644 --- a/x/epochs/integration/action/epoch.go +++ b/x/epochs/integration/action/epoch.go @@ -3,18 +3,20 @@ package action import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/action" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/action" ) type startEpoch struct { epochIdentifier string } -func (s startEpoch) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { +func (s startEpoch) IsNotMandatory() {} + +func (s startEpoch) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error) { epochInfo, err := app.EpochsKeeper.GetEpochInfo(ctx, s.epochIdentifier) if err != nil { - return ctx, err, false + return ctx, err } epochInfo.EpochCountingStarted = true epochInfo.CurrentEpoch = 1 @@ -24,7 +26,7 @@ func (s startEpoch) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, app.EpochsKeeper.Epochs.Insert(ctx, epochInfo.Identifier, epochInfo) - return ctx, nil, false + return ctx, nil } func StartEpoch(epochIdentifier string) action.Action { diff --git a/x/epochs/keeper/epoch.go b/x/epochs/keeper/epoch.go index 48510df30..9a5bc30db 100644 --- a/x/epochs/keeper/epoch.go +++ b/x/epochs/keeper/epoch.go @@ -8,7 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) // GetEpochInfo returns epoch info by identifier. @@ -66,6 +66,7 @@ func (k Keeper) IterateEpochInfo( iterate := k.Epochs.Iterate(ctx, &collections.Range[string]{}) i := int64(0) + defer iterate.Close() for ; iterate.Valid(); iterate.Next() { epoch := iterate.Value() stop := fn(i, epoch) diff --git a/x/epochs/keeper/grpc_query.go b/x/epochs/keeper/grpc_query.go index 3ecf1833a..f6c2d4772 100644 --- a/x/epochs/keeper/grpc_query.go +++ b/x/epochs/keeper/grpc_query.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) var _ types.QueryServer = Querier{} diff --git a/x/epochs/keeper/grpc_query_test.go b/x/epochs/keeper/grpc_query_test.go index c54e66fd7..23f9e6c9d 100644 --- a/x/epochs/keeper/grpc_query_test.go +++ b/x/epochs/keeper/grpc_query_test.go @@ -9,10 +9,10 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/epochs/keeper" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/epochs/keeper" - epochstypes "github.com/NibiruChain/nibiru/x/epochs/types" + epochstypes "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) func TestQueryEpochInfos(t *testing.T) { diff --git a/x/epochs/keeper/hooks_test.go b/x/epochs/keeper/hooks_test.go index 5e4d1d477..ce253cf2f 100644 --- a/x/epochs/keeper/hooks_test.go +++ b/x/epochs/keeper/hooks_test.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/mock" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" ) type MockHooks struct { diff --git a/x/epochs/keeper/keeper.go b/x/epochs/keeper/keeper.go index a1d997938..b6dbc8614 100644 --- a/x/epochs/keeper/keeper.go +++ b/x/epochs/keeper/keeper.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) type Keeper struct { diff --git a/x/epochs/keeper/keeper_test.go b/x/epochs/keeper/keeper_test.go index 046e6d630..43c9b38bf 100644 --- a/x/epochs/keeper/keeper_test.go +++ b/x/epochs/keeper/keeper_test.go @@ -4,20 +4,19 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) func TestUpsertEpochInfo_HappyPath(t *testing.T) { nibiruApp, ctx := testapp.NewNibiruTestAppAndContext() epochInfo := types.EpochInfo{ - Identifier: "year", + Identifier: "bi-monthly", StartTime: time.Time{}, - Duration: time.Hour * 24 * 365, + Duration: time.Hour * 24 * 30 * 2, CurrentEpoch: 0, CurrentEpochStartHeight: 0, CurrentEpochStartTime: time.Time{}, @@ -26,19 +25,19 @@ func TestUpsertEpochInfo_HappyPath(t *testing.T) { nibiruApp.EpochsKeeper.Epochs.Insert(ctx, epochInfo.Identifier, epochInfo) - epochInfoSaved, err := nibiruApp.EpochsKeeper.GetEpochInfo(ctx, "year") + epochInfoSaved, err := nibiruApp.EpochsKeeper.GetEpochInfo(ctx, "bi-monthly") require.NoError(t, err) - assert.EqualValues(t, epochInfo, epochInfoSaved) + require.Equal(t, epochInfo, epochInfoSaved) allEpochs := nibiruApp.EpochsKeeper.AllEpochInfos(ctx) require.Len(t, allEpochs, 5) // Epochs are ordered in alphabetical order require.Equal(t, "30 min", allEpochs[0].Identifier) - require.Equal(t, "day", allEpochs[1].Identifier) - require.Equal(t, "month", allEpochs[2].Identifier) - require.Equal(t, "week", allEpochs[3].Identifier) - require.Equal(t, "year", allEpochs[4].Identifier) + require.Equal(t, "bi-monthly", allEpochs[1].Identifier) + require.Equal(t, "day", allEpochs[2].Identifier) + require.Equal(t, "month", allEpochs[3].Identifier) + require.Equal(t, "week", allEpochs[4].Identifier) } func TestEpochExists(t *testing.T) { diff --git a/x/epochs/module.go b/x/epochs/module.go index 1bbd0a53b..c80517852 100644 --- a/x/epochs/module.go +++ b/x/epochs/module.go @@ -15,10 +15,10 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/epochs/client/cli" - "github.com/NibiruChain/nibiru/x/epochs/keeper" - "github.com/NibiruChain/nibiru/x/epochs/simulation" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/client/cli" + "github.com/NibiruChain/nibiru/v2/x/epochs/keeper" + "github.com/NibiruChain/nibiru/v2/x/epochs/simulation" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) var ( diff --git a/x/epochs/simulation/genesis.go b/x/epochs/simulation/genesis.go index 8a8da4ef1..66adb5fcd 100644 --- a/x/epochs/simulation/genesis.go +++ b/x/epochs/simulation/genesis.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" ) // RandomizedGenState generates a random GenesisState for mint. diff --git a/x/epochs/types/event.pb.go b/x/epochs/types/event.pb.go index b05dc493b..d6dce4a5e 100644 --- a/x/epochs/types/event.pb.go +++ b/x/epochs/types/event.pb.go @@ -134,7 +134,7 @@ func init() { func init() { proto.RegisterFile("nibiru/epochs/v1/event.proto", fileDescriptor_7af14d87a2487e5d) } var fileDescriptor_7af14d87a2487e5d = []byte{ - // 270 bytes of a gzipped FileDescriptorProto + // 273 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xc9, 0xcb, 0x4c, 0xca, 0x2c, 0x2a, 0xd5, 0x4f, 0x2d, 0xc8, 0x4f, 0xce, 0x28, 0xd6, 0x2f, 0x33, 0xd4, 0x4f, 0x2d, 0x4b, 0xcd, 0x2b, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0xc8, 0xea, 0x41, 0x64, 0xf5, @@ -147,11 +147,12 @@ var fileDescriptor_7af14d87a2487e5d = []byte{ 0x93, 0x02, 0xa3, 0x06, 0xb7, 0x91, 0x94, 0x1e, 0xc4, 0x4a, 0x3d, 0x98, 0x95, 0x7a, 0x21, 0x30, 0x2b, 0x9d, 0x38, 0x4e, 0xdc, 0x93, 0x67, 0x98, 0x70, 0x5f, 0x9e, 0x31, 0x88, 0x2f, 0x15, 0x6e, 0x1d, 0x48, 0x5a, 0xc9, 0x88, 0x8b, 0x17, 0xe1, 0x0a, 0xd7, 0xbc, 0x14, 0x22, 0xdc, 0xe0, 0xe4, - 0x76, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, - 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x3a, 0xe9, 0x99, 0x25, 0x19, + 0x79, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, + 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xfa, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x7e, 0xe0, 0x80, 0x72, 0xce, 0x48, 0xcc, 0xcc, 0xd3, - 0x87, 0x06, 0x69, 0x05, 0x2c, 0x50, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x2e, 0x35, - 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x58, 0xd4, 0xcd, 0xf7, 0x72, 0x01, 0x00, 0x00, + 0x87, 0x06, 0x69, 0x99, 0x91, 0x7e, 0x05, 0x2c, 0x5c, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, + 0xc0, 0x8e, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x87, 0x6a, 0xdc, 0x59, 0x75, 0x01, 0x00, + 0x00, } func (m *EventEpochStart) Marshal() (dAtA []byte, err error) { diff --git a/x/epochs/types/genesis.pb.go b/x/epochs/types/genesis.pb.go index 84b413ce0..0c61021ed 100644 --- a/x/epochs/types/genesis.pb.go +++ b/x/epochs/types/genesis.pb.go @@ -77,7 +77,7 @@ func init() { func init() { proto.RegisterFile("nibiru/epochs/v1/genesis.proto", fileDescriptor_0e52385b95ea69b9) } var fileDescriptor_0e52385b95ea69b9 = []byte{ - // 237 bytes of a gzipped FileDescriptorProto + // 240 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcb, 0xcb, 0x4c, 0xca, 0x2c, 0x2a, 0xd5, 0x4f, 0x2d, 0xc8, 0x4f, 0xce, 0x28, 0xd6, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x80, 0xc8, 0xeb, 0x41, @@ -87,12 +87,12 @@ var fileDescriptor_0e52385b95ea69b9 = []byte{ 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x80, 0x2a, 0x90, 0xc1, 0x70, 0x48, 0x71, 0x49, 0x62, 0x49, 0x2a, 0x44, 0x56, 0xc9, 0x93, 0x8b, 0xc7, 0x1d, 0xe2, 0xae, 0x60, 0x90, 0xa8, 0x90, 0x25, 0x17, 0x1b, 0x44, 0xa1, 0x04, 0xa3, 0x02, 0xb3, 0x06, 0xb7, 0x91, 0xb4, 0x1e, 0xba, 0x3b, 0xf5, 0x5c, 0x41, - 0x2c, 0xcf, 0xbc, 0xb4, 0x7c, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xa0, 0x1a, 0x9c, 0xdc, - 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, - 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x27, 0x3d, 0xb3, 0x24, 0xa3, + 0x2c, 0xcf, 0xbc, 0xb4, 0x7c, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xa0, 0x1a, 0x9c, 0x3c, + 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, + 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x3f, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0xdf, 0x0f, 0x6c, 0x9c, 0x73, 0x46, 0x62, 0x66, 0x9e, 0x3e, - 0xd4, 0x65, 0x15, 0x30, 0xb7, 0x95, 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, 0x5d, 0x66, 0x0c, - 0x08, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x99, 0x59, 0x11, 0x42, 0x01, 0x00, 0x00, + 0xd4, 0x65, 0x65, 0x46, 0xfa, 0x15, 0x30, 0xe7, 0x95, 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, + 0x1d, 0x67, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x86, 0xe6, 0x4b, 0x7e, 0x45, 0x01, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/x/epochs/types/hooks_test.go b/x/epochs/types/hooks_test.go index 063843533..57e9f7183 100644 --- a/x/epochs/types/hooks_test.go +++ b/x/epochs/types/hooks_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/mock" - "github.com/NibiruChain/nibiru/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/x/epochs/types/identifier.go b/x/epochs/types/identifier.go index 0450da907..9baf59c13 100644 --- a/x/epochs/types/identifier.go +++ b/x/epochs/types/identifier.go @@ -22,7 +22,7 @@ const ( // ValidateEpochIdentifierInterface performs a stateless // validation of the epoch ID interface. -func ValidateEpochIdentifierInterface(i interface{}) error { +func ValidateEpochIdentifierInterface(i any) error { v, ok := i.(string) if !ok { return fmt.Errorf("invalid parameter type: %T", i) diff --git a/x/epochs/types/query.pb.go b/x/epochs/types/query.pb.go index bab7018fb..7928dfb7f 100644 --- a/x/epochs/types/query.pb.go +++ b/x/epochs/types/query.pb.go @@ -208,33 +208,33 @@ func init() { func init() { proto.RegisterFile("nibiru/epochs/v1/query.proto", fileDescriptor_2d273c3d69b40555) } var fileDescriptor_2d273c3d69b40555 = []byte{ - // 412 bytes of a gzipped FileDescriptorProto + // 413 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x4d, 0x8b, 0xd3, 0x40, - 0x18, 0xce, 0xd4, 0x5a, 0x70, 0xac, 0x20, 0x83, 0x68, 0x8c, 0x35, 0x2d, 0xf1, 0x83, 0x5a, 0x4b, - 0x86, 0xd4, 0x93, 0x9e, 0xa4, 0x45, 0xc1, 0x8b, 0x60, 0xf0, 0xe4, 0x45, 0x26, 0x71, 0x9a, 0x0e, - 0xd8, 0x99, 0x34, 0x33, 0x29, 0xf6, 0xea, 0xc9, 0xa3, 0xb0, 0xec, 0x9f, 0xd8, 0x5f, 0xd2, 0x63, - 0x61, 0x2f, 0x7b, 0x5a, 0x96, 0x76, 0x7f, 0xc8, 0xd2, 0x99, 0x74, 0xb7, 0xdb, 0x0f, 0xe8, 0x2d, - 0x99, 0xe7, 0xe3, 0x7d, 0x9e, 0x77, 0x06, 0xd6, 0x38, 0x8b, 0x58, 0x96, 0x63, 0x9a, 0x8a, 0x78, - 0x20, 0xf1, 0x38, 0xc0, 0xa3, 0x9c, 0x66, 0x13, 0x3f, 0xcd, 0x84, 0x12, 0xe8, 0xa1, 0x41, 0x7d, - 0x83, 0xfa, 0xe3, 0xc0, 0x79, 0x94, 0x88, 0x44, 0x68, 0x10, 0x2f, 0xbf, 0x0c, 0xcf, 0xa9, 0x25, - 0x42, 0x24, 0xbf, 0x29, 0x26, 0x29, 0xc3, 0x84, 0x73, 0xa1, 0x88, 0x62, 0x82, 0xcb, 0x02, 0x6d, - 0xc5, 0x42, 0x0e, 0x85, 0xc4, 0x11, 0x91, 0xd4, 0xd8, 0xe3, 0x71, 0x10, 0x51, 0x45, 0x02, 0x9c, - 0x92, 0x84, 0x71, 0x4d, 0x5e, 0x39, 0x6d, 0xe5, 0x91, 0x8a, 0x28, 0x6a, 0x50, 0xcf, 0x86, 0x8f, - 0xbf, 0x2d, 0xf5, 0x9f, 0x96, 0xe8, 0x17, 0xde, 0x17, 0x32, 0xa4, 0xa3, 0x9c, 0x4a, 0xe5, 0x7d, - 0x87, 0x4f, 0xb6, 0x10, 0x99, 0x0a, 0x2e, 0x29, 0x7a, 0x0f, 0x2b, 0xc6, 0xcd, 0x06, 0x8d, 0x3b, - 0xcd, 0xfb, 0x9d, 0x67, 0xfe, 0x66, 0x2b, 0xff, 0x5a, 0xd5, 0x2d, 0x4f, 0xcf, 0xeb, 0x56, 0x58, - 0x08, 0xbc, 0x0f, 0xd0, 0xd6, 0xae, 0xbd, 0x3c, 0xcb, 0x28, 0x57, 0x9a, 0x56, 0x4c, 0x44, 0x2e, - 0x84, 0xec, 0x17, 0xe5, 0x8a, 0xf5, 0x19, 0xcd, 0x6c, 0xd0, 0x00, 0xcd, 0x7b, 0xe1, 0xda, 0x89, - 0xf7, 0x11, 0x3e, 0xdd, 0xa1, 0x2d, 0x32, 0xbd, 0x80, 0x0f, 0x62, 0x73, 0xfe, 0x53, 0x8f, 0xd2, - 0xfa, 0x72, 0x58, 0x8d, 0xd7, 0xc8, 0x9d, 0x93, 0x12, 0xbc, 0xab, 0x2d, 0xd0, 0x3f, 0x00, 0xe1, - 0x4d, 0x33, 0xd4, 0xdc, 0x6e, 0xb0, 0x7b, 0x2d, 0xce, 0x9b, 0x03, 0x98, 0x26, 0x92, 0xf7, 0xea, - 0xef, 0xe9, 0xe5, 0x51, 0xa9, 0x8e, 0x9e, 0xe3, 0xcd, 0x2b, 0x30, 0x57, 0x65, 0x7e, 0xd1, 0x31, - 0x80, 0xd5, 0xf5, 0x4a, 0xa8, 0xb5, 0x67, 0xc4, 0x8e, 0x9d, 0x39, 0x6f, 0x0f, 0xe2, 0x16, 0x81, - 0xda, 0x3a, 0xd0, 0x6b, 0xf4, 0x72, 0x4f, 0xa0, 0x5b, 0x0b, 0xec, 0x7e, 0x9e, 0xce, 0x5d, 0x30, - 0x9b, 0xbb, 0xe0, 0x62, 0xee, 0x82, 0xff, 0x0b, 0xd7, 0x9a, 0x2d, 0x5c, 0xeb, 0x6c, 0xe1, 0x5a, - 0x3f, 0xda, 0x09, 0x53, 0x83, 0x3c, 0xf2, 0x63, 0x31, 0xc4, 0x5f, 0xb5, 0x53, 0x6f, 0x40, 0x18, - 0x5f, 0xb9, 0xfe, 0x59, 0xf9, 0xaa, 0x49, 0x4a, 0x65, 0x54, 0xd1, 0x2f, 0xed, 0xdd, 0x55, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xe7, 0x96, 0x70, 0xa3, 0x19, 0x03, 0x00, 0x00, + 0x18, 0xce, 0xd4, 0x5a, 0x70, 0xac, 0x20, 0x83, 0x68, 0x8c, 0x35, 0x2d, 0xf1, 0x83, 0x5a, 0x25, + 0x43, 0xea, 0x49, 0x4f, 0xd2, 0xe2, 0xa1, 0x17, 0xc1, 0xe0, 0xc9, 0x8b, 0x4c, 0xe2, 0x34, 0x1d, + 0xb0, 0x33, 0x69, 0x66, 0x12, 0xec, 0xd5, 0x93, 0x47, 0x41, 0xfc, 0x13, 0xfe, 0x92, 0x1e, 0x0b, + 0x7b, 0xd9, 0xd3, 0xb2, 0xb4, 0xfb, 0x43, 0x96, 0xce, 0xa4, 0xbb, 0xdd, 0x7e, 0x40, 0x6f, 0xc9, + 0x3c, 0x1f, 0xef, 0xf3, 0xbc, 0x33, 0xb0, 0xc1, 0x59, 0xc4, 0xb2, 0x1c, 0xd3, 0x54, 0xc4, 0x23, + 0x89, 0x8b, 0x00, 0x4f, 0x72, 0x9a, 0x4d, 0xfd, 0x34, 0x13, 0x4a, 0xa0, 0xfb, 0x06, 0xf5, 0x0d, + 0xea, 0x17, 0x81, 0xf3, 0x20, 0x11, 0x89, 0xd0, 0x20, 0x5e, 0x7d, 0x19, 0x9e, 0xd3, 0x48, 0x84, + 0x48, 0x7e, 0x50, 0x4c, 0x52, 0x86, 0x09, 0xe7, 0x42, 0x11, 0xc5, 0x04, 0x97, 0x25, 0xda, 0x89, + 0x85, 0x1c, 0x0b, 0x89, 0x23, 0x22, 0xa9, 0xb1, 0xc7, 0x45, 0x10, 0x51, 0x45, 0x02, 0x9c, 0x92, + 0x84, 0x71, 0x4d, 0x5e, 0x3b, 0xed, 0xe4, 0x91, 0x8a, 0x28, 0x6a, 0x50, 0xcf, 0x86, 0x0f, 0x3f, + 0xaf, 0xf4, 0x1f, 0x57, 0xe8, 0x80, 0x0f, 0x85, 0x0c, 0xe9, 0x24, 0xa7, 0x52, 0x79, 0x5f, 0xe0, + 0xa3, 0x1d, 0x44, 0xa6, 0x82, 0x4b, 0x8a, 0xde, 0xc1, 0x9a, 0x71, 0xb3, 0x41, 0xeb, 0x56, 0xfb, + 0x6e, 0xf7, 0x89, 0xbf, 0xdd, 0xca, 0xbf, 0x52, 0xf5, 0xaa, 0xb3, 0xb3, 0xa6, 0x15, 0x96, 0x02, + 0xef, 0x3d, 0xb4, 0xb5, 0x6b, 0x3f, 0xcf, 0x32, 0xca, 0x95, 0xa6, 0x95, 0x13, 0x91, 0x0b, 0x21, + 0xfb, 0x4e, 0xb9, 0x62, 0x43, 0x46, 0x33, 0x1b, 0xb4, 0x40, 0xfb, 0x4e, 0xb8, 0x71, 0xe2, 0x7d, + 0x80, 0x8f, 0xf7, 0x68, 0xcb, 0x4c, 0xcf, 0xe0, 0xbd, 0xd8, 0x9c, 0x7f, 0xd3, 0xa3, 0xb4, 0xbe, + 0x1a, 0xd6, 0xe3, 0x0d, 0x72, 0xf7, 0x7f, 0x05, 0xde, 0xd6, 0x16, 0xe8, 0x37, 0x80, 0xf0, 0xba, + 0x19, 0x6a, 0xef, 0x36, 0xd8, 0xbf, 0x16, 0xe7, 0xd5, 0x11, 0x4c, 0x13, 0xc9, 0x7b, 0xf1, 0xeb, + 0xe4, 0xe2, 0x6f, 0xa5, 0x89, 0x9e, 0xe2, 0xed, 0x2b, 0x30, 0x57, 0x65, 0x7e, 0xd1, 0x3f, 0x00, + 0xeb, 0x9b, 0x95, 0x50, 0xe7, 0xc0, 0x88, 0x3d, 0x3b, 0x73, 0x5e, 0x1f, 0xc5, 0x2d, 0x03, 0xbd, + 0xd1, 0x81, 0x5e, 0xa2, 0xe7, 0x07, 0x02, 0xdd, 0x58, 0x60, 0x6f, 0x30, 0x5b, 0xb8, 0x60, 0xbe, + 0x70, 0xc1, 0xf9, 0xc2, 0x05, 0x7f, 0x96, 0xae, 0x35, 0x5f, 0xba, 0xd6, 0xe9, 0xd2, 0xb5, 0xbe, + 0xe2, 0x84, 0xa9, 0x51, 0x1e, 0xf9, 0xb1, 0x18, 0xe3, 0x4f, 0xda, 0xa9, 0x3f, 0x22, 0x8c, 0xaf, + 0x5d, 0x8b, 0x2e, 0xfe, 0xb9, 0xb6, 0x56, 0xd3, 0x94, 0xca, 0xa8, 0xa6, 0x1f, 0xdb, 0xdb, 0xcb, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x48, 0x31, 0x41, 0xe0, 0x1c, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/epochs/types/state.pb.go b/x/epochs/types/state.pb.go index d4a2c07ac..0209b3c38 100644 --- a/x/epochs/types/state.pb.go +++ b/x/epochs/types/state.pb.go @@ -135,35 +135,35 @@ func init() { func init() { proto.RegisterFile("nibiru/epochs/v1/state.proto", fileDescriptor_8bd50db1722dd5e6) } var fileDescriptor_8bd50db1722dd5e6 = []byte{ - // 433 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xb1, 0x6f, 0xd4, 0x30, - 0x14, 0xc6, 0x63, 0x7a, 0x94, 0x9e, 0x01, 0x01, 0x56, 0x81, 0x10, 0x81, 0x13, 0x85, 0x25, 0x12, - 0x55, 0xac, 0x03, 0x26, 0xd8, 0xae, 0x80, 0x60, 0x61, 0x48, 0x19, 0x10, 0xcb, 0x29, 0xc9, 0xf9, - 0x12, 0x4b, 0x8d, 0x1d, 0x25, 0x2f, 0x15, 0xb7, 0x31, 0x33, 0x75, 0xe4, 0x4f, 0xea, 0xd8, 0x91, - 0x29, 0xa0, 0xbb, 0x8d, 0xb1, 0x7f, 0x01, 0x8a, 0x9d, 0x94, 0x83, 0x82, 0xd8, 0x92, 0xf7, 0xfb, - 0xde, 0xf7, 0xf9, 0x3d, 0x3d, 0x7c, 0x5f, 0x8a, 0x44, 0x54, 0x0d, 0xe3, 0xa5, 0x4a, 0xf3, 0x9a, - 0x1d, 0x4d, 0x58, 0x0d, 0x31, 0xf0, 0xb0, 0xac, 0x14, 0x28, 0x72, 0xd3, 0xd0, 0xd0, 0xd0, 0xf0, - 0x68, 0xe2, 0xec, 0x66, 0x2a, 0x53, 0x1a, 0xb2, 0xee, 0xcb, 0xe8, 0x1c, 0x9a, 0x29, 0x95, 0x1d, - 0x72, 0xa6, 0xff, 0x92, 0x66, 0xc1, 0xe6, 0x4d, 0x15, 0x83, 0x50, 0xb2, 0xe7, 0xee, 0x9f, 0x1c, - 0x44, 0xc1, 0x6b, 0x88, 0x8b, 0xd2, 0x08, 0xfc, 0xcf, 0x23, 0x3c, 0x7e, 0xd9, 0x85, 0xbc, 0x91, - 0x0b, 0x45, 0x28, 0xc6, 0x62, 0xce, 0x25, 0x88, 0x85, 0xe0, 0x95, 0x8d, 0x3c, 0x14, 0x8c, 0xa3, - 0x8d, 0x0a, 0x79, 0x8f, 0x71, 0x0d, 0x71, 0x05, 0xb3, 0xce, 0xc6, 0xbe, 0xe4, 0xa1, 0xe0, 0xea, - 0x63, 0x27, 0x34, 0x19, 0xe1, 0x90, 0x11, 0xbe, 0x1b, 0x32, 0xa6, 0x0f, 0x4e, 0x5a, 0xd7, 0x3a, - 0x6b, 0xdd, 0x5b, 0xcb, 0xb8, 0x38, 0x7c, 0xe6, 0xff, 0xea, 0xf5, 0x8f, 0xbf, 0xb9, 0x28, 0x1a, - 0xeb, 0x42, 0x27, 0x27, 0x39, 0xde, 0x19, 0x9e, 0x6e, 0x6f, 0x69, 0xdf, 0x7b, 0x17, 0x7c, 0x5f, - 0xf4, 0x82, 0xe9, 0xa4, 0xb3, 0xfd, 0xd1, 0xba, 0x64, 0x68, 0xd9, 0x53, 0x85, 0x00, 0x5e, 0x94, - 0xb0, 0x3c, 0x6b, 0xdd, 0x1b, 0x26, 0x6c, 0x60, 0xfe, 0x97, 0x2e, 0xea, 0xdc, 0x9d, 0x3c, 0xc4, - 0xd7, 0xd3, 0xa6, 0xaa, 0xb8, 0x84, 0x99, 0xde, 0xae, 0x3d, 0xf2, 0x50, 0x30, 0x8a, 0xae, 0xf5, - 0x45, 0xbd, 0x0c, 0xf2, 0x09, 0x61, 0xfb, 0x37, 0xd5, 0x6c, 0x63, 0xee, 0xcb, 0xff, 0x9d, 0xfb, - 0x51, 0x3f, 0xb7, 0x6b, 0x9e, 0xf2, 0x2f, 0x27, 0xb3, 0x85, 0xdb, 0x9b, 0xc9, 0x07, 0xe7, 0x1b, - 0x79, 0x8a, 0xef, 0x18, 0x7d, 0xaa, 0x1a, 0x09, 0x42, 0x66, 0xa6, 0x91, 0xcf, 0xed, 0x6d, 0x0f, - 0x05, 0x3b, 0xd1, 0xae, 0xa6, 0xfb, 0x3d, 0x3c, 0x30, 0x8c, 0x3c, 0xc7, 0xce, 0xdf, 0xd2, 0x72, - 0x2e, 0xb2, 0x1c, 0xec, 0x2b, 0x1e, 0x0a, 0xb6, 0xa2, 0xbb, 0x17, 0x02, 0x5f, 0x6b, 0x3c, 0x7d, - 0x75, 0xb2, 0xa2, 0xe8, 0x74, 0x45, 0xd1, 0xf7, 0x15, 0x45, 0xc7, 0x6b, 0x6a, 0x9d, 0xae, 0xa9, - 0xf5, 0x75, 0x4d, 0xad, 0x0f, 0x7b, 0x99, 0x80, 0xbc, 0x49, 0xc2, 0x54, 0x15, 0xec, 0xad, 0x3e, - 0xcd, 0xfd, 0x3c, 0x16, 0x92, 0xf5, 0x47, 0xfc, 0x71, 0x38, 0x63, 0x58, 0x96, 0xbc, 0x4e, 0xb6, - 0xf5, 0x4a, 0x9e, 0xfc, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x02, 0xe6, 0x18, 0x2d, 0xe4, 0x02, 0x00, - 0x00, + // 435 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xb1, 0x6f, 0xd3, 0x40, + 0x14, 0xc6, 0x73, 0x34, 0x94, 0xe6, 0x00, 0x01, 0xa7, 0x02, 0xc6, 0x82, 0xb3, 0x65, 0x16, 0x4b, + 0x20, 0x9f, 0x52, 0x98, 0x60, 0x4b, 0x41, 0xa2, 0x0b, 0x83, 0xcb, 0x80, 0x58, 0x22, 0xdb, 0xb9, + 0xd8, 0x27, 0xd5, 0x77, 0x96, 0xfd, 0x1c, 0x91, 0x8d, 0x99, 0xa9, 0x23, 0x7f, 0x52, 0xc7, 0x8e, + 0x4c, 0x06, 0x25, 0x1b, 0x63, 0xff, 0x02, 0xe4, 0x3b, 0xbb, 0x04, 0x02, 0x62, 0xb3, 0xdf, 0xef, + 0x7b, 0xdf, 0x77, 0xef, 0xe9, 0xe1, 0x87, 0x52, 0xc4, 0xa2, 0xac, 0x19, 0x2f, 0x54, 0x92, 0x55, + 0x6c, 0x31, 0x66, 0x15, 0x44, 0xc0, 0x83, 0xa2, 0x54, 0xa0, 0xc8, 0x6d, 0x43, 0x03, 0x43, 0x83, + 0xc5, 0xd8, 0xde, 0x4f, 0x55, 0xaa, 0x34, 0x64, 0xed, 0x97, 0xd1, 0xd9, 0x34, 0x55, 0x2a, 0x3d, + 0xe1, 0x4c, 0xff, 0xc5, 0xf5, 0x9c, 0xcd, 0xea, 0x32, 0x02, 0xa1, 0x64, 0xc7, 0x9d, 0x3f, 0x39, + 0x88, 0x9c, 0x57, 0x10, 0xe5, 0x85, 0x11, 0x78, 0x9f, 0x87, 0x78, 0xf4, 0xba, 0x0d, 0x39, 0x92, + 0x73, 0x45, 0x28, 0xc6, 0x62, 0xc6, 0x25, 0x88, 0xb9, 0xe0, 0xa5, 0x85, 0x5c, 0xe4, 0x8f, 0xc2, + 0x8d, 0x0a, 0x79, 0x8f, 0x71, 0x05, 0x51, 0x09, 0xd3, 0xd6, 0xc6, 0xba, 0xe2, 0x22, 0xff, 0xfa, + 0x81, 0x1d, 0x98, 0x8c, 0xa0, 0xcf, 0x08, 0xde, 0xf5, 0x19, 0x93, 0x47, 0x67, 0x8d, 0x33, 0xb8, + 0x68, 0x9c, 0x3b, 0xcb, 0x28, 0x3f, 0x79, 0xe1, 0xfd, 0xea, 0xf5, 0x4e, 0xbf, 0x39, 0x28, 0x1c, + 0xe9, 0x42, 0x2b, 0x27, 0x19, 0xde, 0xeb, 0x9f, 0x6e, 0xed, 0x68, 0xdf, 0x07, 0x5b, 0xbe, 0xaf, + 0x3a, 0xc1, 0x64, 0xdc, 0xda, 0xfe, 0x68, 0x1c, 0xd2, 0xb7, 0x3c, 0x55, 0xb9, 0x00, 0x9e, 0x17, + 0xb0, 0xbc, 0x68, 0x9c, 0x5b, 0x26, 0xac, 0x67, 0xde, 0x97, 0x36, 0xea, 0xd2, 0x9d, 0x3c, 0xc6, + 0x37, 0x93, 0xba, 0x2c, 0xb9, 0x84, 0xa9, 0xde, 0xae, 0x35, 0x74, 0x91, 0x3f, 0x0c, 0x6f, 0x74, + 0x45, 0xbd, 0x0c, 0xf2, 0x09, 0x61, 0xeb, 0x37, 0xd5, 0x74, 0x63, 0xee, 0xab, 0xff, 0x9d, 0xfb, + 0x49, 0x37, 0xb7, 0x63, 0x9e, 0xf2, 0x2f, 0x27, 0xb3, 0x85, 0xbb, 0x9b, 0xc9, 0xc7, 0x97, 0x1b, + 0x79, 0x8e, 0xef, 0x19, 0x7d, 0xa2, 0x6a, 0x09, 0x42, 0xa6, 0xa6, 0x91, 0xcf, 0xac, 0x5d, 0x17, + 0xf9, 0x7b, 0xe1, 0xbe, 0xa6, 0x87, 0x1d, 0x3c, 0x36, 0x8c, 0xbc, 0xc4, 0xf6, 0xdf, 0xd2, 0x32, + 0x2e, 0xd2, 0x0c, 0xac, 0x6b, 0x2e, 0xf2, 0x77, 0xc2, 0xfb, 0x5b, 0x81, 0x6f, 0x34, 0x9e, 0x1c, + 0x9d, 0xad, 0x28, 0x3a, 0x5f, 0x51, 0xf4, 0x7d, 0x45, 0xd1, 0xe9, 0x9a, 0x0e, 0xce, 0xd7, 0x74, + 0xf0, 0x75, 0x4d, 0x07, 0x1f, 0x58, 0x2a, 0x20, 0xab, 0xe3, 0x20, 0x51, 0x39, 0x7b, 0xab, 0x4f, + 0xf3, 0x30, 0x8b, 0x84, 0x64, 0xdd, 0x11, 0x2f, 0x0e, 0xd8, 0xc7, 0xfe, 0x92, 0x61, 0x59, 0xf0, + 0x2a, 0xde, 0xd5, 0x5b, 0x79, 0xf6, 0x33, 0x00, 0x00, 0xff, 0xff, 0x11, 0xb3, 0x3e, 0x23, 0xe7, + 0x02, 0x00, 0x00, } func (m *EpochInfo) Marshal() (dAtA []byte, err error) { diff --git a/x/evm/README.md b/x/evm/README.md new file mode 100644 index 000000000..12509d421 --- /dev/null +++ b/x/evm/README.md @@ -0,0 +1,15 @@ +# evm + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run README.ts +``` + +This project was created using `bun init` in bun v1.0.28. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/x/evm/chain_config.go b/x/evm/chain_config.go new file mode 100644 index 000000000..eb3662d8a --- /dev/null +++ b/x/evm/chain_config.go @@ -0,0 +1,50 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "math/big" + + errorsmod "cosmossdk.io/errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" +) + +// EthereumConfig returns an Ethereum ChainConfig for EVM state transitions. +func EthereumConfig(chainID *big.Int) *params.ChainConfig { + return ¶ms.ChainConfig{ + ChainID: chainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiBlock: nil, // TODO: change this if we upgrade go-ethereum dependency + CancunBlock: nil, // TODO: change this if we upgrade go-ethereum dependency + TerminalTotalDifficulty: nil, + Ethash: nil, + Clique: nil, + } +} + +// Validate performs a basic validation of the ChainConfig params. The function will return an error +// if any of the block values is uninitialized (i.e. nil) or if the EIP150Hash is an invalid hash. +func Validate() error { + // NOTE: chain ID is not needed to check config order + if err := EthereumConfig(nil).CheckConfigForkOrder(); err != nil { + return errorsmod.Wrap(err, "invalid config fork order") + } + return nil +} diff --git a/x/evm/chain_config_test.go b/x/evm/chain_config_test.go new file mode 100644 index 000000000..faa0364c7 --- /dev/null +++ b/x/evm/chain_config_test.go @@ -0,0 +1,12 @@ +package evm + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestChainConfigValidate(t *testing.T) { + err := Validate() + require.NoError(t, err) +} diff --git a/x/evm/cli/cli_setup_test.go b/x/evm/cli/cli_setup_test.go new file mode 100644 index 000000000..21b11786e --- /dev/null +++ b/x/evm/cli/cli_setup_test.go @@ -0,0 +1,115 @@ +package cli_test + +import ( + "context" + "io" + "testing" + + "github.com/stretchr/testify/suite" + + rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" + sdkclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdktestutil "github.com/cosmos/cosmos-sdk/testutil" + sdktestutilcli "github.com/cosmos/cosmos-sdk/testutil/cli" + testutilmod "github.com/cosmos/cosmos-sdk/types/module/testutil" + + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + + "github.com/NibiruChain/nibiru/v2/x/evm/cli" + "github.com/NibiruChain/nibiru/v2/x/evm/evmmodule" +) + +type Suite struct { + suite.Suite + + keyring keyring.Keyring + encCfg testutilmod.TestEncodingConfig + baseCtx sdkclient.Context + clientCtx sdkclient.Context + + testAcc sdktestutil.TestAccount +} + +func (s *Suite) SetupSuite() { + s.encCfg = testutilmod.MakeTestEncodingConfig(evmmodule.AppModuleBasic{}) + s.keyring = keyring.NewInMemory(s.encCfg.Codec) + s.baseCtx = sdkclient.Context{}. + WithKeyring(s.keyring). + WithTxConfig(s.encCfg.TxConfig). + WithCodec(s.encCfg.Codec). + WithClient(sdktestutilcli.MockTendermintRPC{Client: rpcclientmock.Client{}}). + WithAccountRetriever(sdkclient.MockAccountRetriever{}). + WithOutput(io.Discard). + WithChainID("test-chain") + + s.clientCtx = s.baseCtx + + testAccs := sdktestutil.CreateKeyringAccounts(s.T(), s.keyring, 1) + s.testAcc = testAccs[0] +} + +func TestSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} + +// Flags for broadcasting transactions +func commonTxArgs() []string { + return []string{ + "--yes=true", // skip confirmation + "--broadcast-mode=sync", + "--fees=1unibi", + "--chain-id=test-chain", + } +} + +type TestCase struct { + name string + args []string + extraArgs []string + wantErr string +} + +func (tc TestCase) NewCtx(s *Suite) sdkclient.Context { + return s.baseCtx +} + +func (tc TestCase) RunTxCmd(s *Suite) { + s.Run(tc.name, func() { + ctx := svrcmd.CreateExecuteContext(context.Background()) + + cmd := cli.GetTxCmd() + cmd.SetContext(ctx) + args := append(tc.args, commonTxArgs()...) + cmd.SetArgs(append(args, tc.extraArgs...)) + + s.Require().NoError(sdkclient.SetCmdClientContextHandler(tc.NewCtx(s), cmd)) + + err := cmd.Execute() + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) +} + +func (tc TestCase) RunQueryCmd(s *Suite) { + s.Run(tc.name, func() { + ctx := svrcmd.CreateExecuteContext(context.Background()) + + cmd := cli.GetQueryCmd() + cmd.SetContext(ctx) + args := tc.args // don't append common tx args + cmd.SetArgs(append(args, tc.extraArgs...)) + + s.Require().NoError(sdkclient.SetCmdClientContextHandler(tc.NewCtx(s), cmd)) + + err := cmd.Execute() + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) +} diff --git a/x/evm/cli/cli_test.go b/x/evm/cli/cli_test.go new file mode 100644 index 000000000..99b8b684e --- /dev/null +++ b/x/evm/cli/cli_test.go @@ -0,0 +1,173 @@ +package cli_test + +import ( + "fmt" + "math/big" + + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +var ( + dummyAccs = evmtest.NewEthPrivAccs(3) + dummyEthAddr = dummyAccs[1].EthAddr.Hex() + dummyFuntoken = evm.NewFunToken( + gethcommon.BigToAddress(big.NewInt(123)), + "ibc/testtoken", + false, + ) +) + +func (s *Suite) TestCmdConvertCoinToEvm() { + testCases := []TestCase{ + { + name: "happy: convert-coin-to-evm", + args: []string{ + "convert-coin-to-evm", + dummyEthAddr, + fmt.Sprintf("%d%s", 123, dummyFuntoken.BankDenom), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "", + }, + { + name: "sad: coin format", + args: []string{ + "convert-coin-to-evm", + dummyAccs[1].EthAddr.Hex(), + fmt.Sprintf("%s %d", dummyFuntoken.BankDenom, 123), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "invalid decimal coin expression", + }, + } + + for _, tc := range testCases { + tc.RunTxCmd(s) + } +} + +func (s *Suite) TestCmdCreateFunToken() { + testCases := []TestCase{ + { + name: "happy: create-funtoken (erc20)", + args: []string{ + "create-funtoken", + fmt.Sprintf("--erc20=%s", dummyEthAddr), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "", + }, + { + name: "happy: create-funtoken (bank coin)", + args: []string{ + "create-funtoken", + fmt.Sprintf("--bank-denom=%s", dummyFuntoken.BankDenom), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "", + }, + { + name: "sad: too many args", + args: []string{ + "create-funtoken", + fmt.Sprintf("--erc20=%s", dummyEthAddr), + fmt.Sprintf("--bank-denom=%s", dummyFuntoken.BankDenom), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "exactly one of the flags --bank-denom or --erc20 must be specified", + }, + } + + for _, tc := range testCases { + tc.RunTxCmd(s) + } +} + +func (s *Suite) TestCmdQueryAccount() { + testCases := []TestCase{ + { + name: "happy: query account (bech32)", + args: []string{ + "account", + dummyAccs[0].NibiruAddr.String(), + }, + wantErr: "", + }, + { + name: "happy: query account (eth hex)", + args: []string{ + "account", + dummyAccs[0].EthAddr.Hex(), + }, + wantErr: "", + }, + { + name: "happy: query account (eth hex) --offline", + args: []string{ + "account", + dummyAccs[0].EthAddr.Hex(), + "--offline", + }, + wantErr: "", + }, + { + name: "happy: query account (bech32) --offline", + args: []string{ + "account", + dummyAccs[0].NibiruAddr.String(), + "--offline", + }, + wantErr: "", + }, + { + name: "sad: too many args", + args: []string{ + "funtoken", + "arg1", + "arg2", + }, + wantErr: "accepts 1 arg", + }, + } + + for _, tc := range testCases { + tc.RunQueryCmd(s) + } +} + +func (s *Suite) TestCmdQueryFunToken() { + testCases := []TestCase{ + { + name: "happy: query funtoken (bank coin denom)", + args: []string{ + "funtoken", + dummyFuntoken.BankDenom, + }, + wantErr: "", + }, + { + name: "happy: query funtoken (erc20 addr)", + args: []string{ + "funtoken", + dummyFuntoken.Erc20Addr.String(), + }, + wantErr: "", + }, + { + name: "sad: too many args", + args: []string{ + "funtoken", + "arg1", + "arg2", + }, + wantErr: "accepts 1 arg", + }, + } + + for _, tc := range testCases { + tc.RunQueryCmd(s) + } +} diff --git a/x/evm/cli/query.go b/x/evm/cli/query.go new file mode 100644 index 000000000..545b683d2 --- /dev/null +++ b/x/evm/cli/query.go @@ -0,0 +1,131 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// GetQueryCmd returns a cli command for this module's queries +func GetQueryCmd() *cobra.Command { + moduleQueryCmd := &cobra.Command{ + Use: evm.ModuleName, + Short: fmt.Sprintf( + "Query commands for the x/%s module", evm.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + // Add subcommands + cmds := []*cobra.Command{ + CmdQueryFunToken(), + CmdQueryAccount(), + } + for _, cmd := range cmds { + moduleQueryCmd.AddCommand(cmd) + } + return moduleQueryCmd +} + +// CmdQueryFunToken returns fungible token mapping for either bank coin or erc20 addr +func CmdQueryFunToken() *cobra.Command { + cmd := &cobra.Command{ + Use: "funtoken [coin-or-erc20addr]", + Short: "Query evm fungible token mapping", + Long: strings.TrimSpace( + fmt.Sprintf(`Query evm fungible token mapping. + +Examples: +$ %s query %s get-fun-token ibc/abcdef +$ %s query %s get-fun-token 0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6 +`, + version.AppName, evm.ModuleName, + version.AppName, evm.ModuleName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := evm.NewQueryClient(clientCtx) + + res, err := queryClient.FunTokenMapping(cmd.Context(), &evm.QueryFunTokenMappingRequest{ + Token: args[0], + }) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +func CmdQueryAccount() *cobra.Command { + cmd := &cobra.Command{ + Use: "account [address]", + Short: "Query account by its hex address or bech32", + Long: strings.TrimSpace(""), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := evm.NewQueryClient(clientCtx) + + req := &evm.QueryEthAccountRequest{ + Address: args[0], + } + + isBech32, err := req.Validate() + if err != nil { + return err + } + + offline, _ := cmd.Flags().GetBool("offline") + + if offline { + var addrEth gethcommon.Address + var addrBech32 sdk.AccAddress + + if isBech32 { + addrBech32 = sdk.MustAccAddressFromBech32(req.Address) + addrEth = eth.NibiruAddrToEthAddr(addrBech32) + } else { + addrEth = gethcommon.HexToAddress(req.Address) + addrBech32 = eth.EthAddrToNibiruAddr(addrEth) + } + + resp := new(evm.QueryEthAccountResponse) + resp.EthAddress = addrEth.Hex() + resp.Bech32Address = addrBech32.String() + return clientCtx.PrintProto(resp) + } + + resp, err := queryClient.EthAccount(cmd.Context(), req) + if err != nil { + return fmt.Errorf("consider using the \"--offline\" flag: %w", err) + } + + return clientCtx.PrintProto(resp) + }, + } + cmd.Flags().Bool("offline", false, "Skip the query and only return addresses.") + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/evm/cli/tx.go b/x/evm/cli/tx.go new file mode 100644 index 000000000..dbda5f7fd --- /dev/null +++ b/x/evm/cli/tx.go @@ -0,0 +1,125 @@ +package cli + +import ( + "fmt" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + + "github.com/spf13/cobra" +) + +// GetTxCmd returns a cli command for this module's transactions +func GetTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: evm.ModuleName, + Short: fmt.Sprintf("x/%s transaction subcommands", evm.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmds := []*cobra.Command{ + CmdCreateFunToken(), + CmdConvertCoinToEvm(), + } + for _, cmd := range cmds { + txCmd.AddCommand(cmd) + } + + return txCmd +} + +// CmdCreateFunToken broadcast MsgCreateFunToken +func CmdCreateFunToken() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-funtoken [flags]", + Short: `Create a fungible token mapping between a bank coin and erc20 contract"`, + Long: heredoc.Doc(` + Example: Creating a fungible token mapping from bank coin. + + create-funtoken --bank-denom="ibc/..." + + Example: Creating a fungible token mapping from an ERC20. + + create-funtoken --erc20=[erc20-address] + `), + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + bankDenom, _ := cmd.Flags().GetString("bank-denom") + erc20AddrStr, _ := cmd.Flags().GetString("erc20") + + if (bankDenom == "" && erc20AddrStr == "") || + (bankDenom != "" && erc20AddrStr != "") { + return fmt.Errorf("exactly one of the flags --bank-denom or --erc20 must be specified") + } + + msg := &evm.MsgCreateFunToken{ + Sender: clientCtx.GetFromAddress().String(), + } + if bankDenom != "" { + if err := sdk.ValidateDenom(bankDenom); err != nil { + return err + } + msg.FromBankDenom = bankDenom + } else { + erc20Addr, err := eth.NewEIP55AddrFromStr(erc20AddrStr) + if err != nil { + return err + } + msg.FromErc20 = &erc20Addr + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + cmd.Flags().String("bank-denom", "", "The bank denom to create a fungible token from") + cmd.Flags().String("erc20", "", "The ERC20 address to create a fungible token from") + + return cmd +} + +// CmdConvertCoinToEvm broadcast MsgConvertCoinToEvm +func CmdConvertCoinToEvm() *cobra.Command { + cmd := &cobra.Command{ + Use: "convert-coin-to-evm [to_eth_addr] [coin] [flags]", + Short: `Convert bank [coin] to its erc20 representation and send to the [to_eth_addr] account"`, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + eip55Addr, err := eth.NewEIP55AddrFromStr(args[0]) + if err != nil { + return err + } + + coin, err := sdk.ParseCoinNormalized(args[1]) + if err != nil { + return err + } + msg := &evm.MsgConvertCoinToEvm{ + Sender: clientCtx.GetFromAddress().String(), + BankCoin: coin, + ToEthAddr: eip55Addr, + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/evm/codec.go b/x/evm/codec.go new file mode 100644 index 000000000..a8dff5d4e --- /dev/null +++ b/x/evm/codec.go @@ -0,0 +1,84 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/gogoproto/proto" +) + +var ( + amino = codec.NewLegacyAmino() + // AminoCdc is a amino codec created to support amino JSON compatible msgs. + AminoCdc = codec.NewAminoCodec(amino) +) + +// NOTE: This is required for the GetSignBytes function +func init() { + RegisterLegacyAminoCodec(amino) + amino.Seal() +} + +// RegisterInterfaces registers the client interfaces to protobuf Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*tx.TxExtensionOptionI)(nil), + &ExtensionOptionsEthereumTx{}, + ) + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgEthereumTx{}, + &MsgUpdateParams{}, + ) + registry.RegisterInterface( + "eth.evm.v1.TxData", + (*TxData)(nil), + &DynamicFeeTx{}, + &AccessListTx{}, + &LegacyTx{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +// PackTxData constructs a new Any packed with the given tx data value. It returns +// an error if the client state can't be cast to a protobuf message or if the concrete +// implementation is not registered to the protobuf codec. +func PackTxData(txData TxData) (*codectypes.Any, error) { + msg, ok := txData.(proto.Message) + if !ok { + return nil, errorsmod.Wrapf(errortypes.ErrPackAny, "cannot proto marshal %T", txData) + } + + anyTxData, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, errorsmod.Wrap(errortypes.ErrPackAny, err.Error()) + } + + return anyTxData, nil +} + +// UnpackTxData unpacks an Any into a TxData. It returns an error if the +// client state can't be unpacked into a TxData. +func UnpackTxData(any *codectypes.Any) (TxData, error) { + if any == nil { + return nil, errorsmod.Wrap(errortypes.ErrUnpackAny, "protobuf Any message cannot be nil") + } + + txData, ok := any.GetCachedValue().(TxData) + if !ok { + return nil, errorsmod.Wrapf(errortypes.ErrUnpackAny, "cannot unpack Any into TxData %T", any) + } + + return txData, nil +} + +// RegisterLegacyAminoCodec required for EIP-712 +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgUpdateParams{}, updateParamsName, nil) +} diff --git a/x/evm/const.go b/x/evm/const.go new file mode 100644 index 000000000..0d86ca185 --- /dev/null +++ b/x/evm/const.go @@ -0,0 +1,155 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "fmt" + "math/big" + + "github.com/NibiruChain/collections" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gethcommon "github.com/ethereum/go-ethereum/common" +) + +// BASE_FEE_MICRONIBI is the global base fee value for the network. It has a +// constant value of 1 unibi (micronibi) == 10^12 wei. +var ( + BASE_FEE_MICRONIBI = big.NewInt(1) + BASE_FEE_WEI = NativeToWei(BASE_FEE_MICRONIBI) +) + +const ( + // ModuleName string name of module + ModuleName = "evm" + + // StoreKey: Persistent storage key for ethereum storage data, account code + // (StateDB) or block related data for the Eth Web3 API. + StoreKey = ModuleName + + // TransientKey is the key to access the EVM transient store, that is reset + // during the Commit phase. + TransientKey = "transient_" + ModuleName + + // RouterKey uses module name for routing + RouterKey = ModuleName +) + +// prefix bytes for the EVM persistent store +const ( + KeyPrefixAccCodes collections.Namespace = iota + 1 + KeyPrefixAccState + KeyPrefixParams + KeyPrefixEthAddrIndex + + // KV store prefix for `FunToken` mappings + KeyPrefixFunTokens + // KV store prefix for indexing `FunToken` by ERC-20 address + KeyPrefixFunTokenIdxErc20 + // KV store prefix for indexing `FunToken` by bank coin denomination + KeyPrefixFunTokenIdxBankDenom +) + +// KVStore transient prefix namespaces for the EVM Module. Transient stores only +// remain for current block, and have more gas efficient read and write access. +const ( + NamespaceBlockBloom collections.Namespace = iota + 1 + NamespaceBlockTxIndex + NamespaceBlockLogSize + NamespaceBlockGasUsed +) + +var KeyPrefixBzAccState = KeyPrefixAccState.Prefix() + +// PrefixAccStateEthAddr returns a prefix to iterate over a given account storage. +func PrefixAccStateEthAddr(address gethcommon.Address) []byte { + return append(KeyPrefixBzAccState, address.Bytes()...) +} + +// StateKey defines the full key under which an account state is stored. +func StateKey(address gethcommon.Address, key []byte) []byte { + return append(PrefixAccStateEthAddr(address), key...) +} + +const ( + // Amino names + updateParamsName = "evm/MsgUpdateParams" +) + +type CallType int + +const ( + // CallTypeRPC call type is used on requests to eth_estimateGas rpc API endpoint + CallTypeRPC CallType = iota + 1 + // CallTypeSmart call type is used in case of smart contract methods calls + CallTypeSmart +) + +var ( + EVM_MODULE_ADDRESS gethcommon.Address + EVM_MODULE_ADDRESS_NIBI sdk.AccAddress +) + +func init() { + EVM_MODULE_ADDRESS_NIBI = authtypes.NewModuleAddress(ModuleName) + EVM_MODULE_ADDRESS = gethcommon.BytesToAddress(EVM_MODULE_ADDRESS_NIBI) +} + +// NativeToWei converts a "unibi" amount to "wei" units for the EVM. +// +// Micronibi, labeled "unibi", is the base denomination for NIBI. For NIBI to be +// considered "ether" by Ethereum clients, we need to follow the constraint +// equation: 1 NIBI = 10^18 wei. +// +// Since 1 NIBI = 10^6 micronibi = 10^6 unibi, the following is true: +// 10^0 unibi == 10^12 wei +func NativeToWei(evmDenomAmount *big.Int) (weiAmount *big.Int) { + pow10 := new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil) + return new(big.Int).Mul(evmDenomAmount, pow10) +} + +// WeiToNative converts a "wei" amount to "unibi" units. +// +// Micronibi, labeled "unibi", is the base denomination for NIBI. For NIBI to be +// considered "ether" by Ethereum clients, we need to follow the constraint +// equation: 1 NIBI = 10^18 wei. +// +// Since 1 NIBI = 10^6 micronibi = 10^6 unibi, the following is true: +// 10^0 unibi == 10^12 wei +func WeiToNative(weiAmount *big.Int) (evmDenomAmount *big.Int) { + pow10 := new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil) + return new(big.Int).Quo(weiAmount, pow10) +} + +// ParseWeiAsMultipleOfMicronibi truncates the given wei amount to the highest +// multiple of 1 micronibi (10^12 wei). It returns the truncated value and an +// error if the input value is too small. +// +// Args: +// - weiInt (*big.Int): The amount of wei to be parsed. +// +// Returns: +// - newWeiInt (*big.Int): The truncated amount of wei, which is a multiple of 1 micronibi. +// - err (error): An error indicating if the input value is within the range +// (1, 10^12) inclusive. +// +// Example: +// +// Input number: 123456789012345678901234567890 +// Parsed number: 123456789012 * 10^12 +func ParseWeiAsMultipleOfMicronibi(weiInt *big.Int) (newWeiInt *big.Int, err error) { + // if "weiValue" is nil, 0, or negative, early return + if weiInt == nil || !(weiInt.Cmp(big.NewInt(0)) > 0) { + return weiInt, nil + } + + // err if weiInt is too small + tenPow12 := new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil) + if weiInt.Cmp(tenPow12) < 0 { + return weiInt, fmt.Errorf( + "wei amount is too small (%s), cannot transfer less than 1 micronibi. 10^18 wei == 1 NIBI == 10^6 micronibi", weiInt) + } + + // truncate to highest micronibi amount + newWeiInt = NativeToWei(WeiToNative(weiInt)) + return newWeiInt, nil +} diff --git a/x/evm/deps.go b/x/evm/deps.go new file mode 100644 index 000000000..2325def18 --- /dev/null +++ b/x/evm/deps.go @@ -0,0 +1,38 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// AccountKeeper defines the expected account keeper interface +type AccountKeeper interface { + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + + // GetModuleAccount gets the module account from the auth account store, if the + // account does not exist in the AccountKeeper, then it is created. This + // differs from the "GetModuleAddress" function, which performs a pure + // computation. + GetModuleAccount(ctx sdk.Context, moduleName string) authtypes.ModuleAccountI + + // GetModuleAddress returns an address based on the module name, however it + // does not modify state at all. To create initialize the module account, + // instead use "GetModuleAccount". + GetModuleAddress(moduleName string) sdk.AccAddress + GetAllAccounts(ctx sdk.Context) (accounts []authtypes.AccountI) + IterateAccounts(ctx sdk.Context, cb func(account authtypes.AccountI) bool) + GetSequence(sdk.Context, sdk.AccAddress) (uint64, error) + GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI + SetAccount(ctx sdk.Context, account authtypes.AccountI) + RemoveAccount(ctx sdk.Context, account authtypes.AccountI) + GetParams(ctx sdk.Context) (params authtypes.Params) + SetModuleAccount(ctx sdk.Context, macc authtypes.ModuleAccountI) +} + +// StakingKeeper returns the historical headers kept in store. +type StakingKeeper interface { + GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) + GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator stakingtypes.Validator, found bool) +} diff --git a/x/evm/embeds/.gitignore b/x/evm/embeds/.gitignore new file mode 100644 index 000000000..dc5a8d4cb --- /dev/null +++ b/x/evm/embeds/.gitignore @@ -0,0 +1,22 @@ +node_modules +.env + +ignition +bun.lockb + +# Hardhat files +/cache +artifacts/@openzeppelin +artifacts/build-info +*.dbg.json + +# TypeChain files +/typechain +/typechain-types + +# solidity-coverage files +/coverage +/coverage.json + +# Hardhat Ignition default folder for deployments against a local node +ignition/deployments/chain-31337 diff --git a/x/evm/embeds/HACKING.md b/x/evm/embeds/HACKING.md new file mode 100644 index 000000000..c9789debd --- /dev/null +++ b/x/evm/embeds/HACKING.md @@ -0,0 +1,112 @@ +# x/evm/embeds/HACKING.md + +- [Building Outputs](#building-outputs) +- [Precompile Solidity Documentation](#precompile-solidity-documentation) + - [Comments](#comments) + - [NatSpec Fields](#natspec-fields) +- [Solidity Conventions](#solidity-conventions) + +## Building Outputs + +Workhorse command +```bash +just gen-embeds +``` + +From inside the "Nibiru/x/evm/embeds" directory +```bash +npm install +npx hardhat compile +``` + +## Precompile Solidity Documentation + +Example of a well-documented contract: [[Uniswap/v4-core/.../IHooks.sol](https://github.com/Uniswap/v4-core/blob/3407bce4b39869fe41ad5ec724b2df308c34900f/src/interfaces/IHooks.sol)] + +### Comments + +You should use `///` for Solidity comments to document code in the NatSpec +(Ethereum Natural Specification) format. Many tools like Solidity IDEs, plugins, +and documentation generators use NatSpec comments. + +### NatSpec Fields + +- `@notice`: Used to explain to end users what the function does. Should be written in plain English and focus on the function's purpose. + Best practice: Include for all public and external functions. +- `@param`: Describes a function parameter. Should explain what the parameter is used for. + Best practice: Include for all function parameters, especially in interfaces. +- `@dev`: Provides additional details for developers. Used for implementation details, notes, or warnings for developers. + Best practice: Use when there's important information that doesn't fit in `@notice` but is crucial for developers. +- `@return`: Describes what a function returns. + Best practice: Use for all functions that return values, explaining each return value. + +Example from IHooks.sol: +```solidity +/// @notice The hook called before liquidity is removed +/// @param sender The initial msg.sender for the remove liquidity call +/// @param key The key for the pool +/// @param params The parameters for removing liquidity +/// @param hookData Arbitrary data handed into the PoolManager by the liquidity provider to be be passed on to the hook +/// @return bytes4 The function selector for the hook +function beforeRemoveLiquidity( + address sender, + PoolKey calldata key, + IPoolManager.ModifyLiquidityParams calldata params, + bytes calldata hookData +) external returns (bytes4); +``` + +@inheritdoc: + +Used to inherit documentation from a parent contract or interface. +Best practice: Use when you want to reuse documentation from a base contract. + + +@title: + +Provides a title for the contract or interface. +Best practice: Include at the top of each contract or interface file. + + +@author: + +States the author of the contract. +Best practice: Optional, but can be useful in larger projects. + +## Solidity Conventions + +### State Mutability + +State mutability defines how a function interacts with the blockchain state. Always explicitly declare non-default state mutability keywords for clarity and correctness. + +1. `view` : For stateful queries + - Reads state, but cannot modify it. + - Use for getters or queries. + + ```solidity + function getBalance(address account) external view returns (uint256); + ``` + +2. `pure` : For stateless queries + - Neither reads nor modifies state. + - Use for calculations or logic relying only on inputs. + + ```solidity + function add(uint256 a, uint256 b) external pure returns (uint256); + ``` + +3. `nonpayable` : (Default) State mutating operation + - Modifies state but cannot receive Ether. + - Default if no mutability is specified. + + ```solidity + function updateBalance(address account, uint256 amount) external; + ``` + +4. `payable` : State mutating operation that can receive Ether (NIBI) + - Can receive Ether and may modify state. + - Use for deposits or payments. + + ```solidity + function deposit() external payable; + ``` diff --git a/x/evm/embeds/README.md b/x/evm/embeds/README.md new file mode 100644 index 000000000..6c34c4db3 --- /dev/null +++ b/x/evm/embeds/README.md @@ -0,0 +1,18 @@ +# @nibiruchain/solidity + +Nibiru EVM solidity contracts and ABIs for Nibiru-specific precompiles and core protocol functionality. + +## Install + +```bash +yarn add @nibiruchain/solidity + +# OR npm install OR bun install +``` + +Solidity code is in "`@nibiruchain/solidity/contracts/*`", and +ABI JSON files are in "`@nibiruchain/solidity/abi/*`". + +## Hacking + +[Hacking - Nibiru EVM Solidity Embeds](./HACKING.md) diff --git a/x/evm/embeds/abi/ChainLinkAggregatorV3Interface.json b/x/evm/embeds/abi/ChainLinkAggregatorV3Interface.json new file mode 100644 index 000000000..ddc375519 --- /dev/null +++ b/x/evm/embeds/abi/ChainLinkAggregatorV3Interface.json @@ -0,0 +1,113 @@ +[ + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/x/evm/embeds/abi/ERC20Minter.json b/x/evm/embeds/abi/ERC20Minter.json new file mode 100644 index 000000000..e79d7bb1e --- /dev/null +++ b/x/evm/embeds/abi/ERC20Minter.json @@ -0,0 +1,412 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnFromAuthority", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/x/evm/embeds/abi/IFunToken.json b/x/evm/embeds/abi/IFunToken.json new file mode 100644 index 000000000..1cc1fd6e8 --- /dev/null +++ b/x/evm/embeds/abi/IFunToken.json @@ -0,0 +1,243 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "eventType", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "abciEvent", + "type": "string" + } + ], + "name": "AbciEvent", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + }, + { + "internalType": "address", + "name": "funtoken", + "type": "address" + } + ], + "name": "balance", + "outputs": [ + { + "internalType": "uint256", + "name": "erc20Balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bankBalance", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "erc20", + "type": "address" + }, + { + "internalType": "string", + "name": "bankDenom", + "type": "string" + } + ], + "internalType": "struct IFunToken.FunToken", + "name": "token", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "ethAddr", + "type": "address" + }, + { + "internalType": "string", + "name": "bech32Addr", + "type": "string" + } + ], + "internalType": "struct IFunToken.NibiruAccount", + "name": "whoAddrs", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + }, + { + "internalType": "string", + "name": "bankDenom", + "type": "string" + } + ], + "name": "bankBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "bankBalance", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "ethAddr", + "type": "address" + }, + { + "internalType": "string", + "name": "bech32Addr", + "type": "string" + } + ], + "internalType": "struct IFunToken.NibiruAccount", + "name": "whoAddrs", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "to", + "type": "string" + }, + { + "internalType": "string", + "name": "bankDenom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "bankMsgSend", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "erc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "to", + "type": "string" + } + ], + "name": "sendToBank", + "outputs": [ + { + "internalType": "uint256", + "name": "sentAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "bankDenom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "to", + "type": "string" + } + ], + "name": "sendToEvm", + "outputs": [ + { + "internalType": "uint256", + "name": "sentAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "who", + "type": "string" + } + ], + "name": "whoAmI", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "ethAddr", + "type": "address" + }, + { + "internalType": "string", + "name": "bech32Addr", + "type": "string" + } + ], + "internalType": "struct IFunToken.NibiruAccount", + "name": "whoAddrs", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/x/evm/embeds/abi/INibiruEvm.json b/x/evm/embeds/abi/INibiruEvm.json new file mode 100644 index 000000000..04b61a528 --- /dev/null +++ b/x/evm/embeds/abi/INibiruEvm.json @@ -0,0 +1,21 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "eventType", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "abciEvent", + "type": "string" + } + ], + "name": "AbciEvent", + "type": "event" + } +] \ No newline at end of file diff --git a/x/evm/embeds/abi/IOracle.json b/x/evm/embeds/abi/IOracle.json new file mode 100644 index 000000000..f81f8c679 --- /dev/null +++ b/x/evm/embeds/abi/IOracle.json @@ -0,0 +1,70 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "pair", + "type": "string" + } + ], + "name": "chainLinkLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "pair", + "type": "string" + } + ], + "name": "queryExchangeRate", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "blockTimeMs", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/x/evm/embeds/abi/IWasm.json b/x/evm/embeds/abi/IWasm.json new file mode 100644 index 000000000..7d7020d67 --- /dev/null +++ b/x/evm/embeds/abi/IWasm.json @@ -0,0 +1,214 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "eventType", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "abciEvent", + "type": "string" + } + ], + "name": "AbciEvent", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "string", + "name": "denom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct INibiruEvm.BankCoin[]", + "name": "funds", + "type": "tuple[]" + } + ], + "name": "execute", + "outputs": [ + { + "internalType": "bytes", + "name": "response", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "string", + "name": "denom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct INibiruEvm.BankCoin[]", + "name": "funds", + "type": "tuple[]" + } + ], + "internalType": "struct IWasm.WasmExecuteMsg[]", + "name": "executeMsgs", + "type": "tuple[]" + } + ], + "name": "executeMulti", + "outputs": [ + { + "internalType": "bytes[]", + "name": "responses", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "admin", + "type": "string" + }, + { + "internalType": "uint64", + "name": "codeID", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + }, + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "components": [ + { + "internalType": "string", + "name": "denom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct INibiruEvm.BankCoin[]", + "name": "funds", + "type": "tuple[]" + } + ], + "name": "instantiate", + "outputs": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "req", + "type": "bytes" + } + ], + "name": "query", + "outputs": [ + { + "internalType": "bytes", + "name": "response", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "key", + "type": "bytes" + } + ], + "name": "queryRaw", + "outputs": [ + { + "internalType": "bytes", + "name": "response", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/x/evm/embeds/abi/NibiruOracleChainLinkLike.json b/x/evm/embeds/abi/NibiruOracleChainLinkLike.json new file mode 100644 index 000000000..3d5f83213 --- /dev/null +++ b/x/evm/embeds/abi/NibiruOracleChainLinkLike.json @@ -0,0 +1,155 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "_pair", + "type": "string" + }, + { + "internalType": "uint8", + "name": "_dec", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "_decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pair", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } +] \ No newline at end of file diff --git a/x/evm/embeds/artifacts/contracts/ERC20Minter.sol/ERC20Minter.json b/x/evm/embeds/artifacts/contracts/ERC20Minter.sol/ERC20Minter.json new file mode 100644 index 000000000..6e95f5739 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/ERC20Minter.sol/ERC20Minter.json @@ -0,0 +1,421 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ERC20Minter", + "sourceName": "contracts/ERC20Minter.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burnFromAuthority", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b50604051620022fd380380620022fd833981810160405281019062000037919062000356565b828281600390816200004a91906200063b565b5080600490816200005c91906200063b565b5050506200007f620000736200009960201b60201c565b620000a160201b60201c565b62000090816200016760201b60201c565b50505062000722565b600033905090565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b80600560146101000a81548160ff021916908360ff16021790555050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001ee82620001a3565b810181811067ffffffffffffffff8211171562000210576200020f620001b4565b5b80604052505050565b60006200022562000185565b9050620002338282620001e3565b919050565b600067ffffffffffffffff821115620002565762000255620001b4565b5b6200026182620001a3565b9050602081019050919050565b60005b838110156200028e57808201518184015260208101905062000271565b60008484015250505050565b6000620002b1620002ab8462000238565b62000219565b905082815260208101848484011115620002d057620002cf6200019e565b5b620002dd8482856200026e565b509392505050565b600082601f830112620002fd57620002fc62000199565b5b81516200030f8482602086016200029a565b91505092915050565b600060ff82169050919050565b620003308162000318565b81146200033c57600080fd5b50565b600081519050620003508162000325565b92915050565b6000806000606084860312156200037257620003716200018f565b5b600084015167ffffffffffffffff81111562000393576200039262000194565b5b620003a186828701620002e5565b935050602084015167ffffffffffffffff811115620003c557620003c462000194565b5b620003d386828701620002e5565b9250506040620003e6868287016200033f565b9150509250925092565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200044357607f821691505b602082108103620004595762000458620003fb565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620004c37fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000484565b620004cf868362000484565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200051c620005166200051084620004e7565b620004f1565b620004e7565b9050919050565b6000819050919050565b6200053883620004fb565b62000550620005478262000523565b84845462000491565b825550505050565b600090565b6200056762000558565b620005748184846200052d565b505050565b5b818110156200059c57620005906000826200055d565b6001810190506200057a565b5050565b601f821115620005eb57620005b5816200045f565b620005c08462000474565b81016020851015620005d0578190505b620005e8620005df8562000474565b83018262000579565b50505b505050565b600082821c905092915050565b60006200061060001984600802620005f0565b1980831691505092915050565b60006200062b8383620005fd565b9150826002028217905092915050565b6200064682620003f0565b67ffffffffffffffff811115620006625762000661620001b4565b5b6200066e82546200042a565b6200067b828285620005a0565b600060209050601f831160018114620006b357600084156200069e578287015190505b620006aa85826200061d565b8655506200071a565b601f198416620006c3866200045f565b60005b82811015620006ed57848901518255600182019150602085019450602081019050620006c6565b868310156200070d578489015162000709601f891682620005fd565b8355505b6001600288020188555050505b505050505050565b611bcb80620007326000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c806370a08231116100a257806395d89b411161007157806395d89b41146102cd578063a457c2d7146102eb578063a9059cbb1461031b578063dd62ed3e1461034b578063f2fde38b1461037b57610116565b806370a0823114610259578063715018a61461028957806379cc6790146102935780638da5cb5b146102af57610116565b806324bd8aaf116100e957806324bd8aaf146101b7578063313ce567146101d357806339509351146101f157806340c10f191461022157806342966c681461023d57610116565b806306fdde031461011b578063095ea7b31461013957806318160ddd1461016957806323b872dd14610187575b600080fd5b610123610397565b60405161013091906111c3565b60405180910390f35b610153600480360381019061014e919061127e565b610429565b60405161016091906112d9565b60405180910390f35b61017161044c565b60405161017e9190611303565b60405180910390f35b6101a1600480360381019061019c919061131e565b610456565b6040516101ae91906112d9565b60405180910390f35b6101d160048036038101906101cc919061127e565b610485565b005b6101db61049b565b6040516101e8919061138d565b60405180910390f35b61020b6004803603810190610206919061127e565b6104b2565b60405161021891906112d9565b60405180910390f35b61023b6004803603810190610236919061127e565b6104e9565b005b610257600480360381019061025291906113a8565b6104ff565b005b610273600480360381019061026e91906113d5565b610513565b6040516102809190611303565b60405180910390f35b61029161055b565b005b6102ad60048036038101906102a8919061127e565b61056f565b005b6102b761058f565b6040516102c49190611411565b60405180910390f35b6102d56105b9565b6040516102e291906111c3565b60405180910390f35b6103056004803603810190610300919061127e565b61064b565b60405161031291906112d9565b60405180910390f35b6103356004803603810190610330919061127e565b6106c2565b60405161034291906112d9565b60405180910390f35b6103656004803603810190610360919061142c565b6106e5565b6040516103729190611303565b60405180910390f35b610395600480360381019061039091906113d5565b61076c565b005b6060600380546103a69061149b565b80601f01602080910402602001604051908101604052809291908181526020018280546103d29061149b565b801561041f5780601f106103f45761010080835404028352916020019161041f565b820191906000526020600020905b81548152906001019060200180831161040257829003601f168201915b5050505050905090565b6000806104346107ef565b90506104418185856107f7565b600191505092915050565b6000600254905090565b6000806104616107ef565b905061046e8582856109c0565b610479858585610a4c565b60019150509392505050565b61048d610cc2565b6104978282610d40565b5050565b6000600560149054906101000a900460ff16905090565b6000806104bd6107ef565b90506104de8185856104cf85896106e5565b6104d991906114fb565b6107f7565b600191505092915050565b6104f1610cc2565b6104fb8282610f0d565b5050565b61051061050a6107ef565b82610d40565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b610563610cc2565b61056d6000611063565b565b6105818261057b6107ef565b836109c0565b61058b8282610d40565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600480546105c89061149b565b80601f01602080910402602001604051908101604052809291908181526020018280546105f49061149b565b80156106415780601f1061061657610100808354040283529160200191610641565b820191906000526020600020905b81548152906001019060200180831161062457829003601f168201915b5050505050905090565b6000806106566107ef565b9050600061066482866106e5565b9050838110156106a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106a0906115a1565b60405180910390fd5b6106b682868684036107f7565b60019250505092915050565b6000806106cd6107ef565b90506106da818585610a4c565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610774610cc2565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036107e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107da90611633565b60405180910390fd5b6107ec81611063565b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610866576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085d906116c5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108cc90611757565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516109b39190611303565b60405180910390a3505050565b60006109cc84846106e5565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610a465781811015610a38576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a2f906117c3565b60405180910390fd5b610a4584848484036107f7565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610abb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ab290611855565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610b2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b21906118e7565b60405180910390fd5b610b35838383611129565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610bbb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bb290611979565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610ca99190611303565b60405180910390a3610cbc84848461112e565b50505050565b610cca6107ef565b73ffffffffffffffffffffffffffffffffffffffff16610ce861058f565b73ffffffffffffffffffffffffffffffffffffffff1614610d3e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d35906119e5565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610daf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610da690611a77565b60405180910390fd5b610dbb82600083611129565b60008060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610e41576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e3890611b09565b60405180910390fd5b8181036000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008282540392505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610ef49190611303565b60405180910390a3610f088360008461112e565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610f7c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7390611b75565b60405180910390fd5b610f8860008383611129565b8060026000828254610f9a91906114fb565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161104b9190611303565b60405180910390a361105f6000838361112e565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561116d578082015181840152602081019050611152565b60008484015250505050565b6000601f19601f8301169050919050565b600061119582611133565b61119f818561113e565b93506111af81856020860161114f565b6111b881611179565b840191505092915050565b600060208201905081810360008301526111dd818461118a565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611215826111ea565b9050919050565b6112258161120a565b811461123057600080fd5b50565b6000813590506112428161121c565b92915050565b6000819050919050565b61125b81611248565b811461126657600080fd5b50565b60008135905061127881611252565b92915050565b60008060408385031215611295576112946111e5565b5b60006112a385828601611233565b92505060206112b485828601611269565b9150509250929050565b60008115159050919050565b6112d3816112be565b82525050565b60006020820190506112ee60008301846112ca565b92915050565b6112fd81611248565b82525050565b600060208201905061131860008301846112f4565b92915050565b600080600060608486031215611337576113366111e5565b5b600061134586828701611233565b935050602061135686828701611233565b925050604061136786828701611269565b9150509250925092565b600060ff82169050919050565b61138781611371565b82525050565b60006020820190506113a2600083018461137e565b92915050565b6000602082840312156113be576113bd6111e5565b5b60006113cc84828501611269565b91505092915050565b6000602082840312156113eb576113ea6111e5565b5b60006113f984828501611233565b91505092915050565b61140b8161120a565b82525050565b60006020820190506114266000830184611402565b92915050565b60008060408385031215611443576114426111e5565b5b600061145185828601611233565b925050602061146285828601611233565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806114b357607f821691505b6020821081036114c6576114c561146c565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061150682611248565b915061151183611248565b9250828201905080821115611529576115286114cc565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b600061158b60258361113e565b91506115968261152f565b604082019050919050565b600060208201905081810360008301526115ba8161157e565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b600061161d60268361113e565b9150611628826115c1565b604082019050919050565b6000602082019050818103600083015261164c81611610565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b60006116af60248361113e565b91506116ba82611653565b604082019050919050565b600060208201905081810360008301526116de816116a2565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b600061174160228361113e565b915061174c826116e5565b604082019050919050565b6000602082019050818103600083015261177081611734565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b60006117ad601d8361113e565b91506117b882611777565b602082019050919050565b600060208201905081810360008301526117dc816117a0565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b600061183f60258361113e565b915061184a826117e3565b604082019050919050565b6000602082019050818103600083015261186e81611832565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b60006118d160238361113e565b91506118dc82611875565b604082019050919050565b60006020820190508181036000830152611900816118c4565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b600061196360268361113e565b915061196e82611907565b604082019050919050565b6000602082019050818103600083015261199281611956565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006119cf60208361113e565b91506119da82611999565b602082019050919050565b600060208201905081810360008301526119fe816119c2565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b6000611a6160218361113e565b9150611a6c82611a05565b604082019050919050565b60006020820190508181036000830152611a9081611a54565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b6000611af360228361113e565b9150611afe82611a97565b604082019050919050565b60006020820190508181036000830152611b2281611ae6565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000611b5f601f8361113e565b9150611b6a82611b29565b602082019050919050565b60006020820190508181036000830152611b8e81611b52565b905091905056fea26469706673582212205add6492de6f6eefa6690b922417d13334f680513aa15eb1128c21e044a149e564736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c806370a08231116100a257806395d89b411161007157806395d89b41146102cd578063a457c2d7146102eb578063a9059cbb1461031b578063dd62ed3e1461034b578063f2fde38b1461037b57610116565b806370a0823114610259578063715018a61461028957806379cc6790146102935780638da5cb5b146102af57610116565b806324bd8aaf116100e957806324bd8aaf146101b7578063313ce567146101d357806339509351146101f157806340c10f191461022157806342966c681461023d57610116565b806306fdde031461011b578063095ea7b31461013957806318160ddd1461016957806323b872dd14610187575b600080fd5b610123610397565b60405161013091906111c3565b60405180910390f35b610153600480360381019061014e919061127e565b610429565b60405161016091906112d9565b60405180910390f35b61017161044c565b60405161017e9190611303565b60405180910390f35b6101a1600480360381019061019c919061131e565b610456565b6040516101ae91906112d9565b60405180910390f35b6101d160048036038101906101cc919061127e565b610485565b005b6101db61049b565b6040516101e8919061138d565b60405180910390f35b61020b6004803603810190610206919061127e565b6104b2565b60405161021891906112d9565b60405180910390f35b61023b6004803603810190610236919061127e565b6104e9565b005b610257600480360381019061025291906113a8565b6104ff565b005b610273600480360381019061026e91906113d5565b610513565b6040516102809190611303565b60405180910390f35b61029161055b565b005b6102ad60048036038101906102a8919061127e565b61056f565b005b6102b761058f565b6040516102c49190611411565b60405180910390f35b6102d56105b9565b6040516102e291906111c3565b60405180910390f35b6103056004803603810190610300919061127e565b61064b565b60405161031291906112d9565b60405180910390f35b6103356004803603810190610330919061127e565b6106c2565b60405161034291906112d9565b60405180910390f35b6103656004803603810190610360919061142c565b6106e5565b6040516103729190611303565b60405180910390f35b610395600480360381019061039091906113d5565b61076c565b005b6060600380546103a69061149b565b80601f01602080910402602001604051908101604052809291908181526020018280546103d29061149b565b801561041f5780601f106103f45761010080835404028352916020019161041f565b820191906000526020600020905b81548152906001019060200180831161040257829003601f168201915b5050505050905090565b6000806104346107ef565b90506104418185856107f7565b600191505092915050565b6000600254905090565b6000806104616107ef565b905061046e8582856109c0565b610479858585610a4c565b60019150509392505050565b61048d610cc2565b6104978282610d40565b5050565b6000600560149054906101000a900460ff16905090565b6000806104bd6107ef565b90506104de8185856104cf85896106e5565b6104d991906114fb565b6107f7565b600191505092915050565b6104f1610cc2565b6104fb8282610f0d565b5050565b61051061050a6107ef565b82610d40565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b610563610cc2565b61056d6000611063565b565b6105818261057b6107ef565b836109c0565b61058b8282610d40565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060600480546105c89061149b565b80601f01602080910402602001604051908101604052809291908181526020018280546105f49061149b565b80156106415780601f1061061657610100808354040283529160200191610641565b820191906000526020600020905b81548152906001019060200180831161062457829003601f168201915b5050505050905090565b6000806106566107ef565b9050600061066482866106e5565b9050838110156106a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106a0906115a1565b60405180910390fd5b6106b682868684036107f7565b60019250505092915050565b6000806106cd6107ef565b90506106da818585610a4c565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b610774610cc2565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036107e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107da90611633565b60405180910390fd5b6107ec81611063565b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610866576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085d906116c5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108d5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108cc90611757565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516109b39190611303565b60405180910390a3505050565b60006109cc84846106e5565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610a465781811015610a38576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a2f906117c3565b60405180910390fd5b610a4584848484036107f7565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610abb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ab290611855565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610b2a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b21906118e7565b60405180910390fd5b610b35838383611129565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610bbb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bb290611979565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610ca99190611303565b60405180910390a3610cbc84848461112e565b50505050565b610cca6107ef565b73ffffffffffffffffffffffffffffffffffffffff16610ce861058f565b73ffffffffffffffffffffffffffffffffffffffff1614610d3e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d35906119e5565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610daf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610da690611a77565b60405180910390fd5b610dbb82600083611129565b60008060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610e41576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e3890611b09565b60405180910390fd5b8181036000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600260008282540392505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610ef49190611303565b60405180910390a3610f088360008461112e565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610f7c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7390611b75565b60405180910390fd5b610f8860008383611129565b8060026000828254610f9a91906114fb565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161104b9190611303565b60405180910390a361105f6000838361112e565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561116d578082015181840152602081019050611152565b60008484015250505050565b6000601f19601f8301169050919050565b600061119582611133565b61119f818561113e565b93506111af81856020860161114f565b6111b881611179565b840191505092915050565b600060208201905081810360008301526111dd818461118a565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611215826111ea565b9050919050565b6112258161120a565b811461123057600080fd5b50565b6000813590506112428161121c565b92915050565b6000819050919050565b61125b81611248565b811461126657600080fd5b50565b60008135905061127881611252565b92915050565b60008060408385031215611295576112946111e5565b5b60006112a385828601611233565b92505060206112b485828601611269565b9150509250929050565b60008115159050919050565b6112d3816112be565b82525050565b60006020820190506112ee60008301846112ca565b92915050565b6112fd81611248565b82525050565b600060208201905061131860008301846112f4565b92915050565b600080600060608486031215611337576113366111e5565b5b600061134586828701611233565b935050602061135686828701611233565b925050604061136786828701611269565b9150509250925092565b600060ff82169050919050565b61138781611371565b82525050565b60006020820190506113a2600083018461137e565b92915050565b6000602082840312156113be576113bd6111e5565b5b60006113cc84828501611269565b91505092915050565b6000602082840312156113eb576113ea6111e5565b5b60006113f984828501611233565b91505092915050565b61140b8161120a565b82525050565b60006020820190506114266000830184611402565b92915050565b60008060408385031215611443576114426111e5565b5b600061145185828601611233565b925050602061146285828601611233565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806114b357607f821691505b6020821081036114c6576114c561146c565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061150682611248565b915061151183611248565b9250828201905080821115611529576115286114cc565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b600061158b60258361113e565b91506115968261152f565b604082019050919050565b600060208201905081810360008301526115ba8161157e565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b600061161d60268361113e565b9150611628826115c1565b604082019050919050565b6000602082019050818103600083015261164c81611610565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b60006116af60248361113e565b91506116ba82611653565b604082019050919050565b600060208201905081810360008301526116de816116a2565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b600061174160228361113e565b915061174c826116e5565b604082019050919050565b6000602082019050818103600083015261177081611734565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b60006117ad601d8361113e565b91506117b882611777565b602082019050919050565b600060208201905081810360008301526117dc816117a0565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b600061183f60258361113e565b915061184a826117e3565b604082019050919050565b6000602082019050818103600083015261186e81611832565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b60006118d160238361113e565b91506118dc82611875565b604082019050919050565b60006020820190508181036000830152611900816118c4565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b600061196360268361113e565b915061196e82611907565b604082019050919050565b6000602082019050818103600083015261199281611956565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006119cf60208361113e565b91506119da82611999565b602082019050919050565b600060208201905081810360008301526119fe816119c2565b9050919050565b7f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b6000611a6160218361113e565b9150611a6c82611a05565b604082019050919050565b60006020820190508181036000830152611a9081611a54565b9050919050565b7f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60008201527f6365000000000000000000000000000000000000000000000000000000000000602082015250565b6000611af360228361113e565b9150611afe82611a97565b604082019050919050565b60006020820190508181036000830152611b2281611ae6565b9050919050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000611b5f601f8361113e565b9150611b6a82611b29565b602082019050919050565b60006020820190508181036000830152611b8e81611b52565b905091905056fea26469706673582212205add6492de6f6eefa6690b922417d13334f680513aa15eb1128c21e044a149e564736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/IFunToken.sol/IFunToken.json b/x/evm/embeds/artifacts/contracts/IFunToken.sol/IFunToken.json new file mode 100644 index 000000000..7839f1a89 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/IFunToken.sol/IFunToken.json @@ -0,0 +1,252 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "IFunToken", + "sourceName": "contracts/IFunToken.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "eventType", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "abciEvent", + "type": "string" + } + ], + "name": "AbciEvent", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + }, + { + "internalType": "address", + "name": "funtoken", + "type": "address" + } + ], + "name": "balance", + "outputs": [ + { + "internalType": "uint256", + "name": "erc20Balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bankBalance", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "erc20", + "type": "address" + }, + { + "internalType": "string", + "name": "bankDenom", + "type": "string" + } + ], + "internalType": "struct IFunToken.FunToken", + "name": "token", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "ethAddr", + "type": "address" + }, + { + "internalType": "string", + "name": "bech32Addr", + "type": "string" + } + ], + "internalType": "struct IFunToken.NibiruAccount", + "name": "whoAddrs", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + }, + { + "internalType": "string", + "name": "bankDenom", + "type": "string" + } + ], + "name": "bankBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "bankBalance", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "ethAddr", + "type": "address" + }, + { + "internalType": "string", + "name": "bech32Addr", + "type": "string" + } + ], + "internalType": "struct IFunToken.NibiruAccount", + "name": "whoAddrs", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "to", + "type": "string" + }, + { + "internalType": "string", + "name": "bankDenom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "bankMsgSend", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "erc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "to", + "type": "string" + } + ], + "name": "sendToBank", + "outputs": [ + { + "internalType": "uint256", + "name": "sentAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "bankDenom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "to", + "type": "string" + } + ], + "name": "sendToEvm", + "outputs": [ + { + "internalType": "uint256", + "name": "sentAmount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "who", + "type": "string" + } + ], + "name": "whoAmI", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "ethAddr", + "type": "address" + }, + { + "internalType": "string", + "name": "bech32Addr", + "type": "string" + } + ], + "internalType": "struct IFunToken.NibiruAccount", + "name": "whoAddrs", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/IOracle.sol/ChainLinkAggregatorV3Interface.json b/x/evm/embeds/artifacts/contracts/IOracle.sol/ChainLinkAggregatorV3Interface.json new file mode 100644 index 000000000..0be9cb13e --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/IOracle.sol/ChainLinkAggregatorV3Interface.json @@ -0,0 +1,122 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ChainLinkAggregatorV3Interface", + "sourceName": "contracts/IOracle.sol", + "abi": [ + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "_roundId", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/IOracle.sol/IOracle.json b/x/evm/embeds/artifacts/contracts/IOracle.sol/IOracle.json new file mode 100644 index 000000000..1171c920c --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/IOracle.sol/IOracle.json @@ -0,0 +1,79 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "IOracle", + "sourceName": "contracts/IOracle.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "pair", + "type": "string" + } + ], + "name": "chainLinkLatestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "pair", + "type": "string" + } + ], + "name": "queryExchangeRate", + "outputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "blockTimeMs", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "blockHeight", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuth.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuth.json new file mode 100644 index 000000000..77faf7af4 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuth.json @@ -0,0 +1,97 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSAuth", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "constant": false, + "inputs": [ + { + "name": "owner_", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "authority_", + "type": "address" + } + ], + "name": "setAuthority", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "authority", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "authority", + "type": "address" + } + ], + "name": "LogSetAuthority", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + } + ], + "name": "LogSetOwner", + "type": "event" + } + ], + "bytecode": "0x6060604052341561000f57600080fd5b33600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a261061b806100a26000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806313af4035146100675780637a9e5e4b146100a05780638da5cb5b146100d9578063bf7e214f1461012e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610183565b005b34156100ab57600080fd5b6100d7600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610265565b005b34156100e457600080fd5b6100ec610345565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561013957600080fd5b61014161036b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101b1336000357fffffffff0000000000000000000000000000000000000000000000000000000016610390565b15156101bc57600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b610293336000357fffffffff0000000000000000000000000000000000000000000000000000000016610390565b151561029e57600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156103cf57600190506105e9565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561042e57600190506105e9565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561048d57600090506105e9565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b15156105cb57600080fd5b6102c65a03f115156105dc57600080fd5b5050506040518051905090505b929150505600a165627a7a723058203965e1ecd0781e359ae067027585ec9c736c2bd7a138b809a89be282e0b69b330029", + "deployedBytecode": "0x606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806313af4035146100675780637a9e5e4b146100a05780638da5cb5b146100d9578063bf7e214f1461012e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610183565b005b34156100ab57600080fd5b6100d7600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610265565b005b34156100e457600080fd5b6100ec610345565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561013957600080fd5b61014161036b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101b1336000357fffffffff0000000000000000000000000000000000000000000000000000000016610390565b15156101bc57600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b610293336000357fffffffff0000000000000000000000000000000000000000000000000000000016610390565b151561029e57600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156103cf57600190506105e9565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561042e57600190506105e9565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561048d57600090506105e9565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b15156105cb57600080fd5b6102c65a03f115156105dc57600080fd5b5050506040518051905090505b929150505600a165627a7a723058203965e1ecd0781e359ae067027585ec9c736c2bd7a138b809a89be282e0b69b330029", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuthEvents.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuthEvents.json new file mode 100644 index 000000000..f38110226 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuthEvents.json @@ -0,0 +1,35 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSAuthEvents", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "authority", + "type": "address" + } + ], + "name": "LogSetAuthority", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + } + ], + "name": "LogSetOwner", + "type": "event" + } + ], + "bytecode": "0x60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820482fd205f6d62e7b66ff2e3194cd5a5def77e9460f305ec3bf11ef374fed685d0029", + "deployedBytecode": "0x6060604052600080fd00a165627a7a72305820482fd205f6d62e7b66ff2e3194cd5a5def77e9460f305ec3bf11ef374fed685d0029", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuthority.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuthority.json new file mode 100644 index 000000000..9459aa0d9 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSAuthority.json @@ -0,0 +1,38 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSAuthority", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "constant": true, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "sig", + "type": "bytes4" + } + ], + "name": "canCall", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSMath.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSMath.json new file mode 100644 index 000000000..c279b56f3 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSMath.json @@ -0,0 +1,10 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSMath", + "sourceName": "contracts/MKR.sol", + "abi": [], + "bytecode": "0x60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820d191478102eb59e59dbf8bd1168a079984302f4f957092ac877d210db1a13e420029", + "deployedBytecode": "0x6060604052600080fd00a165627a7a72305820d191478102eb59e59dbf8bd1168a079984302f4f957092ac877d210db1a13e420029", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSNote.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSNote.json new file mode 100644 index 000000000..eceb35851 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSNote.json @@ -0,0 +1,48 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSNote", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "anonymous": true, + "inputs": [ + { + "indexed": true, + "name": "sig", + "type": "bytes4" + }, + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": true, + "name": "foo", + "type": "bytes32" + }, + { + "indexed": true, + "name": "bar", + "type": "bytes32" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + }, + { + "indexed": false, + "name": "fax", + "type": "bytes" + } + ], + "name": "LogNote", + "type": "event" + } + ], + "bytecode": "0x60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820867630c0f37bb81d4b5f15a3e77b04ada6667969a96ddaeea40a5d9bbff0f0b10029", + "deployedBytecode": "0x6060604052600080fd00a165627a7a72305820867630c0f37bb81d4b5f15a3e77b04ada6667969a96ddaeea40a5d9bbff0f0b10029", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSStop.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSStop.json new file mode 100644 index 000000000..acb5f2fcc --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSStop.json @@ -0,0 +1,160 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSStop", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "constant": false, + "inputs": [], + "name": "stop", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "owner_", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stopped", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "authority_", + "type": "address" + } + ], + "name": "setAuthority", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "start", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "authority", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "authority", + "type": "address" + } + ], + "name": "LogSetAuthority", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + } + ], + "name": "LogSetOwner", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": true, + "name": "sig", + "type": "bytes4" + }, + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": true, + "name": "foo", + "type": "bytes32" + }, + { + "indexed": true, + "name": "bar", + "type": "bytes32" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + }, + { + "indexed": false, + "name": "fax", + "type": "bytes" + } + ], + "name": "LogNote", + "type": "event" + } + ], + "bytecode": "0x606060405233600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a26108ab806100976000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806307da68f51461008857806313af40351461009d57806375f12b21146100d65780637a9e5e4b146101035780638da5cb5b1461013c578063be9a655514610191578063bf7e214f146101a6575b600080fd5b341561009357600080fd5b61009b6101fb565b005b34156100a857600080fd5b6100d4600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102fd565b005b34156100e157600080fd5b6100e96103df565b604051808215151515815260200191505060405180910390f35b341561010e57600080fd5b61013a600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506103f2565b005b341561014757600080fd5b61014f6104d2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561019c57600080fd5b6101a46104f8565b005b34156101b157600080fd5b6101b96105fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610229336000357fffffffff0000000000000000000000000000000000000000000000000000000016610620565b151561023457600080fd5b60008060043591506024359050806000191682600019163373ffffffffffffffffffffffffffffffffffffffff166000357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19163460003660405180848152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a460018060146101000a81548160ff0219169083151502179055505050565b61032b336000357fffffffff0000000000000000000000000000000000000000000000000000000016610620565b151561033657600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b600160149054906101000a900460ff1681565b610420336000357fffffffff0000000000000000000000000000000000000000000000000000000016610620565b151561042b57600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610526336000357fffffffff0000000000000000000000000000000000000000000000000000000016610620565b151561053157600080fd5b60008060043591506024359050806000191682600019163373ffffffffffffffffffffffffffffffffffffffff166000357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19163460003660405180848152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a46000600160146101000a81548160ff0219169083151502179055505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561065f5760019050610879565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156106be5760019050610879565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561071d5760009050610879565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b151561085b57600080fd5b6102c65a03f1151561086c57600080fd5b5050506040518051905090505b929150505600a165627a7a72305820e4fb0b3e5634e485d10a52d17139437a37577ad47f9e3b45b7069415d65b10cb0029", + "deployedBytecode": "0x606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806307da68f51461008857806313af40351461009d57806375f12b21146100d65780637a9e5e4b146101035780638da5cb5b1461013c578063be9a655514610191578063bf7e214f146101a6575b600080fd5b341561009357600080fd5b61009b6101fb565b005b34156100a857600080fd5b6100d4600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102fd565b005b34156100e157600080fd5b6100e96103df565b604051808215151515815260200191505060405180910390f35b341561010e57600080fd5b61013a600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506103f2565b005b341561014757600080fd5b61014f6104d2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561019c57600080fd5b6101a46104f8565b005b34156101b157600080fd5b6101b96105fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610229336000357fffffffff0000000000000000000000000000000000000000000000000000000016610620565b151561023457600080fd5b60008060043591506024359050806000191682600019163373ffffffffffffffffffffffffffffffffffffffff166000357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19163460003660405180848152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a460018060146101000a81548160ff0219169083151502179055505050565b61032b336000357fffffffff0000000000000000000000000000000000000000000000000000000016610620565b151561033657600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b600160149054906101000a900460ff1681565b610420336000357fffffffff0000000000000000000000000000000000000000000000000000000016610620565b151561042b57600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610526336000357fffffffff0000000000000000000000000000000000000000000000000000000016610620565b151561053157600080fd5b60008060043591506024359050806000191682600019163373ffffffffffffffffffffffffffffffffffffffff166000357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19163460003660405180848152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a46000600160146101000a81548160ff0219169083151502179055505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561065f5760019050610879565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156106be5760019050610879565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561071d5760009050610879565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b151561085b57600080fd5b6102c65a03f1151561086c57600080fd5b5050506040518051905090505b929150505600a165627a7a72305820e4fb0b3e5634e485d10a52d17139437a37577ad47f9e3b45b7069415d65b10cb0029", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSThing.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSThing.json new file mode 100644 index 000000000..0039cca35 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSThing.json @@ -0,0 +1,128 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSThing", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "constant": false, + "inputs": [ + { + "name": "owner_", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "authority_", + "type": "address" + } + ], + "name": "setAuthority", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "authority", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": true, + "name": "sig", + "type": "bytes4" + }, + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": true, + "name": "foo", + "type": "bytes32" + }, + { + "indexed": true, + "name": "bar", + "type": "bytes32" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + }, + { + "indexed": false, + "name": "fax", + "type": "bytes" + } + ], + "name": "LogNote", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "authority", + "type": "address" + } + ], + "name": "LogSetAuthority", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + } + ], + "name": "LogSetOwner", + "type": "event" + } + ], + "bytecode": "0x606060405233600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a261061b806100976000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806313af4035146100675780637a9e5e4b146100a05780638da5cb5b146100d9578063bf7e214f1461012e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610183565b005b34156100ab57600080fd5b6100d7600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610265565b005b34156100e457600080fd5b6100ec610345565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561013957600080fd5b61014161036b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101b1336000357fffffffff0000000000000000000000000000000000000000000000000000000016610390565b15156101bc57600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b610293336000357fffffffff0000000000000000000000000000000000000000000000000000000016610390565b151561029e57600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156103cf57600190506105e9565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561042e57600190506105e9565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561048d57600090506105e9565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b15156105cb57600080fd5b6102c65a03f115156105dc57600080fd5b5050506040518051905090505b929150505600a165627a7a7230582036876a0f4dc663a0b514bca9f895503a8780cb8105eb4d09213927eb8af442fd0029", + "deployedBytecode": "0x606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806313af4035146100675780637a9e5e4b146100a05780638da5cb5b146100d9578063bf7e214f1461012e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610183565b005b34156100ab57600080fd5b6100d7600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610265565b005b34156100e457600080fd5b6100ec610345565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561013957600080fd5b61014161036b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101b1336000357fffffffff0000000000000000000000000000000000000000000000000000000016610390565b15156101bc57600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b610293336000357fffffffff0000000000000000000000000000000000000000000000000000000016610390565b151561029e57600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156103cf57600190506105e9565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561042e57600190506105e9565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561048d57600090506105e9565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b15156105cb57600080fd5b6102c65a03f115156105dc57600080fd5b5050506040518051905090505b929150505600a165627a7a7230582036876a0f4dc663a0b514bca9f895503a8780cb8105eb4d09213927eb8af442fd0029", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSToken.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSToken.json new file mode 100644 index 000000000..081026c47 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSToken.json @@ -0,0 +1,575 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSToken", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "stop", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "guy", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "owner_", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "guy", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "wad", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "name_", + "type": "bytes32" + } + ], + "name": "setName", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "src", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stopped", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "authority_", + "type": "address" + } + ], + "name": "setAuthority", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "guy", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "wad", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "push", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "move", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "start", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "authority", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "guy", + "type": "address" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "guy", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "pull", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "symbol_", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "authority", + "type": "address" + } + ], + "name": "LogSetAuthority", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + } + ], + "name": "LogSetOwner", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": true, + "name": "sig", + "type": "bytes4" + }, + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": true, + "name": "foo", + "type": "bytes32" + }, + { + "indexed": true, + "name": "bar", + "type": "bytes32" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + }, + { + "indexed": false, + "name": "fax", + "type": "bytes" + } + ], + "name": "LogNote", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + } + ], + "bytecode": "0x606060405260126006556000600790600019169055341561001f57600080fd5b604051602080611a9383398101604052808051906020019091905050600080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550806000819055505033600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a280600581600019169055505061196b806101286000396000f300606060405260043610610149576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde031461014e57806307da68f51461017f578063095ea7b31461019457806313af4035146101ee57806318160ddd1461022757806323b872dd14610250578063313ce567146102c957806340c10f19146102f257806342966c68146103345780635ac801fe1461035757806370a082311461037e57806375f12b21146103cb5780637a9e5e4b146103f85780638da5cb5b1461043157806395d89b41146104865780639dc29fac146104b7578063a0712d68146104f9578063a9059cbb1461051c578063b753a98c14610576578063bb35783b146105b8578063be9a655514610619578063bf7e214f1461062e578063daea85c514610683578063dd62ed3e146106d4578063f2d5d56b14610740575b600080fd5b341561015957600080fd5b610161610782565b60405180826000191660001916815260200191505060405180910390f35b341561018a57600080fd5b610192610788565b005b341561019f57600080fd5b6101d4600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061088b565b604051808215151515815260200191505060405180910390f35b34156101f957600080fd5b610225600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108bb565b005b341561023257600080fd5b61023a61099d565b6040518082815260200191505060405180910390f35b341561025b57600080fd5b6102af600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506109a6565b604051808215151515815260200191505060405180910390f35b34156102d457600080fd5b6102dc610d30565b6040518082815260200191505060405180910390f35b34156102fd57600080fd5b610332600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610d36565b005b341561033f57600080fd5b6103556004808035906020019091905050610e7b565b005b341561036257600080fd5b61037c600480803560001916906020019091905050610e88565b005b341561038957600080fd5b6103b5600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610ecf565b6040518082815260200191505060405180910390f35b34156103d657600080fd5b6103de610f18565b604051808215151515815260200191505060405180910390f35b341561040357600080fd5b61042f600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610f2b565b005b341561043c57600080fd5b61044461100d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561049157600080fd5b610499611033565b60405180826000191660001916815260200191505060405180910390f35b34156104c257600080fd5b6104f7600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050611039565b005b341561050457600080fd5b61051a6004808035906020019091905050611362565b005b341561052757600080fd5b61055c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061136f565b604051808215151515815260200191505060405180910390f35b341561058157600080fd5b6105b6600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050611384565b005b34156105c357600080fd5b610617600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050611394565b005b341561062457600080fd5b61062c6113a5565b005b341561063957600080fd5b6106416114a8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561068e57600080fd5b6106ba600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506114ce565b604051808215151515815260200191505060405180910390f35b34156106df57600080fd5b61072a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061151d565b6040518082815260200191505060405180910390f35b341561074b57600080fd5b610780600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506115a4565b005b60075481565b6107b6336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b15156107c157600080fd5b60008060043591506024359050806000191682600019163373ffffffffffffffffffffffffffffffffffffffff166000357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19163460003660405180848152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a46001600460146101000a81548160ff0219169083151502179055505050565b6000600460149054906101000a900460ff161515156108a957600080fd5b6108b38383611815565b905092915050565b6108e9336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b15156108f457600080fd5b80600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b60008054905090565b6000600460149054906101000a900460ff161515156109c457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614158015610a9c57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b15610ba857610b27600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611907565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b610bf1600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611907565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610c7d600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611923565b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b60065481565b610d64336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b1515610d6f57600080fd5b600460149054906101000a900460ff16151515610d8b57600080fd5b610dd4600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611923565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610e2360005482611923565b6000819055508173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885826040518082815260200191505060405180910390a25050565b610e853382611039565b50565b610eb6336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b1515610ec157600080fd5b806007816000191690555050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600460149054906101000a900460ff1681565b610f59336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b1515610f6457600080fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60055481565b611067336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b151561107257600080fd5b600460149054906101000a900460ff1615151561108e57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415801561116657507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b15611272576111f1600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611907565b600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b6112bb600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611907565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061130a60005482611907565b6000819055508173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5826040518082815260200191505060405180910390a25050565b61136c3382610d36565b50565b600061137c3384846109a6565b905092915050565b61138f3383836109a6565b505050565b61139f8383836109a6565b50505050565b6113d3336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b15156113de57600080fd5b60008060043591506024359050806000191682600019163373ffffffffffffffffffffffffffffffffffffffff166000357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19163460003660405180848152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a46000600460146101000a81548160ff0219169083151502179055505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600460149054906101000a900460ff161515156114ec57600080fd5b611516827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611815565b9050919050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6115af8233836109a6565b505050565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156115f3576001905061180f565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611652576001905061180f565b600073ffffffffffffffffffffffffffffffffffffffff16600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156116b2576000905061180f565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b15156117f157600080fd5b6102c65a03f1151561180257600080fd5b5050506040518051905090505b92915050565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000828284039150811115151561191d57600080fd5b92915050565b6000828284019150811015151561193957600080fd5b929150505600a165627a7a72305820310251b2c6d2735c8890ac55f0b00cbaa61f36e72e80928c01ce0ef6ee0a34010029", + "deployedBytecode": "0x606060405260043610610149576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde031461014e57806307da68f51461017f578063095ea7b31461019457806313af4035146101ee57806318160ddd1461022757806323b872dd14610250578063313ce567146102c957806340c10f19146102f257806342966c68146103345780635ac801fe1461035757806370a082311461037e57806375f12b21146103cb5780637a9e5e4b146103f85780638da5cb5b1461043157806395d89b41146104865780639dc29fac146104b7578063a0712d68146104f9578063a9059cbb1461051c578063b753a98c14610576578063bb35783b146105b8578063be9a655514610619578063bf7e214f1461062e578063daea85c514610683578063dd62ed3e146106d4578063f2d5d56b14610740575b600080fd5b341561015957600080fd5b610161610782565b60405180826000191660001916815260200191505060405180910390f35b341561018a57600080fd5b610192610788565b005b341561019f57600080fd5b6101d4600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061088b565b604051808215151515815260200191505060405180910390f35b34156101f957600080fd5b610225600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108bb565b005b341561023257600080fd5b61023a61099d565b6040518082815260200191505060405180910390f35b341561025b57600080fd5b6102af600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506109a6565b604051808215151515815260200191505060405180910390f35b34156102d457600080fd5b6102dc610d30565b6040518082815260200191505060405180910390f35b34156102fd57600080fd5b610332600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610d36565b005b341561033f57600080fd5b6103556004808035906020019091905050610e7b565b005b341561036257600080fd5b61037c600480803560001916906020019091905050610e88565b005b341561038957600080fd5b6103b5600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610ecf565b6040518082815260200191505060405180910390f35b34156103d657600080fd5b6103de610f18565b604051808215151515815260200191505060405180910390f35b341561040357600080fd5b61042f600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610f2b565b005b341561043c57600080fd5b61044461100d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561049157600080fd5b610499611033565b60405180826000191660001916815260200191505060405180910390f35b34156104c257600080fd5b6104f7600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050611039565b005b341561050457600080fd5b61051a6004808035906020019091905050611362565b005b341561052757600080fd5b61055c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061136f565b604051808215151515815260200191505060405180910390f35b341561058157600080fd5b6105b6600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050611384565b005b34156105c357600080fd5b610617600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050611394565b005b341561062457600080fd5b61062c6113a5565b005b341561063957600080fd5b6106416114a8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561068e57600080fd5b6106ba600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506114ce565b604051808215151515815260200191505060405180910390f35b34156106df57600080fd5b61072a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061151d565b6040518082815260200191505060405180910390f35b341561074b57600080fd5b610780600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506115a4565b005b60075481565b6107b6336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b15156107c157600080fd5b60008060043591506024359050806000191682600019163373ffffffffffffffffffffffffffffffffffffffff166000357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19163460003660405180848152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a46001600460146101000a81548160ff0219169083151502179055505050565b6000600460149054906101000a900460ff161515156108a957600080fd5b6108b38383611815565b905092915050565b6108e9336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b15156108f457600080fd5b80600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b60008054905090565b6000600460149054906101000a900460ff161515156109c457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614158015610a9c57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b15610ba857610b27600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611907565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b610bf1600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611907565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610c7d600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483611923565b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b60065481565b610d64336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b1515610d6f57600080fd5b600460149054906101000a900460ff16151515610d8b57600080fd5b610dd4600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611923565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610e2360005482611923565b6000819055508173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885826040518082815260200191505060405180910390a25050565b610e853382611039565b50565b610eb6336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b1515610ec157600080fd5b806007816000191690555050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600460149054906101000a900460ff1681565b610f59336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b1515610f6457600080fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60055481565b611067336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b151561107257600080fd5b600460149054906101000a900460ff1615151561108e57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415801561116657507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b15611272576111f1600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611907565b600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b6112bb600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482611907565b600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061130a60005482611907565b6000819055508173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5826040518082815260200191505060405180910390a25050565b61136c3382610d36565b50565b600061137c3384846109a6565b905092915050565b61138f3383836109a6565b505050565b61139f8383836109a6565b50505050565b6113d3336000357fffffffff00000000000000000000000000000000000000000000000000000000166115b4565b15156113de57600080fd5b60008060043591506024359050806000191682600019163373ffffffffffffffffffffffffffffffffffffffff166000357fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19163460003660405180848152602001806020018281038252848482818152602001925080828437820191505094505050505060405180910390a46000600460146101000a81548160ff0219169083151502179055505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600460149054906101000a900460ff161515156114ec57600080fd5b611516827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611815565b9050919050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6115af8233836109a6565b505050565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156115f3576001905061180f565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611652576001905061180f565b600073ffffffffffffffffffffffffffffffffffffffff16600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156116b2576000905061180f565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856000604051602001526040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b15156117f157600080fd5b6102c65a03f1151561180257600080fd5b5050506040518051905090505b92915050565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000828284039150811115151561191d57600080fd5b92915050565b6000828284019150811015151561193957600080fd5b929150505600a165627a7a72305820310251b2c6d2735c8890ac55f0b00cbaa61f36e72e80928c01ce0ef6ee0a34010029", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/DSTokenBase.json b/x/evm/embeds/artifacts/contracts/MKR.sol/DSTokenBase.json new file mode 100644 index 000000000..467679d35 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/DSTokenBase.json @@ -0,0 +1,195 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "DSTokenBase", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "constant": false, + "inputs": [ + { + "name": "guy", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "src", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "guy", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "supply", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + } + ], + "bytecode": "0x6060604052341561000f57600080fd5b60405160208061081c8339810160405280805190602001909190505080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508060008190555050610796806100866000396000f300606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063095ea7b31461007d57806318160ddd146100d757806323b872dd1461010057806370a0823114610179578063a9059cbb146101c6578063dd62ed3e14610220575b600080fd5b341561008857600080fd5b6100bd600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061028c565b604051808215151515815260200191505060405180910390f35b34156100e257600080fd5b6100ea61037e565b6040518082815260200191505060405180910390f35b341561010b57600080fd5b61015f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610387565b604051808215151515815260200191505060405180910390f35b341561018457600080fd5b6101b0600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061064d565b6040518082815260200191505060405180910390f35b34156101d157600080fd5b610206600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610696565b604051808215151515815260200191505060405180910390f35b341561022b57600080fd5b610276600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506106ab565b6040518082815260200191505060405180910390f35b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60008054905090565b60003373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415156104c557610444600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483610732565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b61050e600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483610732565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061059a600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548361074e565b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006106a3338484610387565b905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6000828284039150811115151561074857600080fd5b92915050565b6000828284019150811015151561076457600080fd5b929150505600a165627a7a72305820ae0b5dc9f8abfce1f3c6b41085e97c24e95534036623cb14dfb00097a8535d460029", + "deployedBytecode": "0x606060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063095ea7b31461007d57806318160ddd146100d757806323b872dd1461010057806370a0823114610179578063a9059cbb146101c6578063dd62ed3e14610220575b600080fd5b341561008857600080fd5b6100bd600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061028c565b604051808215151515815260200191505060405180910390f35b34156100e257600080fd5b6100ea61037e565b6040518082815260200191505060405180910390f35b341561010b57600080fd5b61015f600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610387565b604051808215151515815260200191505060405180910390f35b341561018457600080fd5b6101b0600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061064d565b6040518082815260200191505060405180910390f35b34156101d157600080fd5b610206600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610696565b604051808215151515815260200191505060405180910390f35b341561022b57600080fd5b610276600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506106ab565b6040518082815260200191505060405180910390f35b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60008054905090565b60003373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415156104c557610444600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483610732565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b61050e600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205483610732565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061059a600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548361074e565b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60006106a3338484610387565b905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6000828284039150811115151561074857600080fd5b92915050565b6000828284019150811015151561076457600080fd5b929150505600a165627a7a72305820ae0b5dc9f8abfce1f3c6b41085e97c24e95534036623cb14dfb00097a8535d460029", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/MKR.sol/ERC20.json b/x/evm/embeds/artifacts/contracts/MKR.sol/ERC20.json new file mode 100644 index 000000000..3563a5c54 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/MKR.sol/ERC20.json @@ -0,0 +1,184 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ERC20", + "sourceName": "contracts/MKR.sol", + "abi": [ + { + "constant": false, + "inputs": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "ok", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "supply", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "ok", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "ok", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "_allowance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/NibiruEvmUtils.sol/INibiruEvm.json b/x/evm/embeds/artifacts/contracts/NibiruEvmUtils.sol/INibiruEvm.json new file mode 100644 index 000000000..9704d7497 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/NibiruEvmUtils.sol/INibiruEvm.json @@ -0,0 +1,30 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "INibiruEvm", + "sourceName": "contracts/NibiruEvmUtils.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "eventType", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "abciEvent", + "type": "string" + } + ], + "name": "AbciEvent", + "type": "event" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/NibiruOracleChainLinkLike.sol/NibiruOracleChainLinkLike.json b/x/evm/embeds/artifacts/contracts/NibiruOracleChainLinkLike.sol/NibiruOracleChainLinkLike.json new file mode 100644 index 000000000..76abd7e4d --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/NibiruOracleChainLinkLike.sol/NibiruOracleChainLinkLike.json @@ -0,0 +1,164 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "NibiruOracleChainLinkLike", + "sourceName": "contracts/NibiruOracleChainLinkLike.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "_pair", + "type": "string" + }, + { + "internalType": "uint8", + "name": "_dec", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "_decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint80", + "name": "", + "type": "uint80" + } + ], + "name": "getRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pair", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b50604051620012c4380380620012c48339818101604052810190620000379190620002ce565b60128160ff16111562000081576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000789062000395565b60405180910390fd5b6000825111620000c8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000bf9062000407565b60405180910390fd5b8160009081620000d9919062000674565b5080600160006101000a81548160ff021916908360ff16021790555050506200075b565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b62000166826200011b565b810181811067ffffffffffffffff821117156200018857620001876200012c565b5b80604052505050565b60006200019d620000fd565b9050620001ab82826200015b565b919050565b600067ffffffffffffffff821115620001ce57620001cd6200012c565b5b620001d9826200011b565b9050602081019050919050565b60005b8381101562000206578082015181840152602081019050620001e9565b60008484015250505050565b6000620002296200022384620001b0565b62000191565b90508281526020810184848401111562000248576200024762000116565b5b62000255848285620001e6565b509392505050565b600082601f83011262000275576200027462000111565b5b81516200028784826020860162000212565b91505092915050565b600060ff82169050919050565b620002a88162000290565b8114620002b457600080fd5b50565b600081519050620002c8816200029d565b92915050565b60008060408385031215620002e857620002e762000107565b5b600083015167ffffffffffffffff8111156200030957620003086200010c565b5b62000317858286016200025d565b92505060206200032a85828601620002b7565b9150509250929050565b600082825260208201905092915050565b7f446563696d616c732063616e6e6f742065786365656420313800000000000000600082015250565b60006200037d60198362000334565b91506200038a8262000345565b602082019050919050565b60006020820190508181036000830152620003b0816200036e565b9050919050565b7f5061697220737472696e672063616e6e6f7420626520656d7074790000000000600082015250565b6000620003ef601b8362000334565b9150620003fc82620003b7565b602082019050919050565b600060208201905081810360008301526200042281620003e0565b9050919050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200047c57607f821691505b60208210810362000492576200049162000434565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620004fc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620004bd565b620005088683620004bd565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620005556200054f620005498462000520565b6200052a565b62000520565b9050919050565b6000819050919050565b620005718362000534565b6200058962000580826200055c565b848454620004ca565b825550505050565b600090565b620005a062000591565b620005ad81848462000566565b505050565b5b81811015620005d557620005c960008262000596565b600181019050620005b3565b5050565b601f8211156200062457620005ee8162000498565b620005f984620004ad565b8101602085101562000609578190505b620006216200061885620004ad565b830182620005b2565b50505b505050565b600082821c905092915050565b6000620006496000198460080262000629565b1980831691505092915050565b600062000664838362000636565b9150826002028217905092915050565b6200067f8262000429565b67ffffffffffffffff8111156200069b576200069a6200012c565b5b620006a7825462000463565b620006b4828285620005d9565b600060209050601f831160018114620006ec5760008415620006d7578287015190505b620006e3858262000656565b86555062000753565b601f198416620006fc8662000498565b60005b828110156200072657848901518255600182019150602085019450602081019050620006ff565b8683101562000746578489015162000742601f89168262000636565b8355505b6001600288020188555050505b505050505050565b610b59806200076b6000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c80637284e4161161005b5780637284e416146100dc5780639a6fc8f5146100fa578063a8aa1b311461012e578063feaf968c1461014c5761007d565b8063313ce5671461008257806332424aa3146100a057806354fd4d50146100be575b600080fd5b61008a61016e565b6040516100979190610393565b60405180910390f35b6100a8610185565b6040516100b59190610393565b60405180910390f35b6100c6610198565b6040516100d391906103c7565b60405180910390f35b6100e46101a1565b6040516100f19190610472565b60405180910390f35b610114600480360381019061010f91906104db565b6101c9565b604051610125959493929190610530565b60405180910390f35b6101366101ec565b6040516101439190610472565b60405180910390f35b61015461027a565b604051610165959493929190610530565b60405180910390f35b6000600160009054906101000a900460ff16905090565b600160009054906101000a900460ff1681565b60006001905090565b606060006040516020016101b591906106f8565b604051602081830303815290604052905090565b60008060008060006101d961027a565b9450945094509450945091939590929450565b600080546101f99061062f565b80601f01602080910402602001604051908101604052809291908181526020018280546102259061062f565b80156102725780601f1061024757610100808354040283529160200191610272565b820191906000526020600020905b81548152906001019060200180831161025557829003601f168201915b505050505081565b60008060008060008060008060008061080173ffffffffffffffffffffffffffffffffffffffff1663ece378ed60006040518263ffffffff1660e01b81526004016102c5919061079e565b60a060405180830381865afa1580156102e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610306919061082d565b9450945094509450945061031984610336565b985084898484849950995099509950995050505050509091929394565b600080600160009054906101000a900460ff16601261035591906108d7565b905080600a6103649190610a3f565b8361036f9190610ab9565b915050919050565b600060ff82169050919050565b61038d81610377565b82525050565b60006020820190506103a86000830184610384565b92915050565b6000819050919050565b6103c1816103ae565b82525050565b60006020820190506103dc60008301846103b8565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561041c578082015181840152602081019050610401565b60008484015250505050565b6000601f19601f8301169050919050565b6000610444826103e2565b61044e81856103ed565b935061045e8185602086016103fe565b61046781610428565b840191505092915050565b6000602082019050818103600083015261048c8184610439565b905092915050565b600080fd5b600069ffffffffffffffffffff82169050919050565b6104b881610499565b81146104c357600080fd5b50565b6000813590506104d5816104af565b92915050565b6000602082840312156104f1576104f0610494565b5b60006104ff848285016104c6565b91505092915050565b61051181610499565b82525050565b6000819050919050565b61052a81610517565b82525050565b600060a0820190506105456000830188610508565b6105526020830187610521565b61055f60408301866103b8565b61056c60608301856103b8565b6105796080830184610508565b9695505050505050565b600081905092915050565b7f4e6962697275204f7261636c6520436861696e4c696e6b2d6c696b652070726960008201527f6365206665656420666f72200000000000000000000000000000000000000000602082015250565b60006105ea602c83610583565b91506105f58261058e565b602c82019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061064757607f821691505b60208210810361065a57610659610600565b5b50919050565b60008190508160005260206000209050919050565b600081546106828161062f565b61068c8186610583565b945060018216600081146106a757600181146106bc576106ef565b60ff19831686528115158202860193506106ef565b6106c585610660565b60005b838110156106e7578154818901526001820191506020810190506106c8565b838801955050505b50505092915050565b6000610703826105dd565b915061070f8284610675565b915081905092915050565b600081546107278161062f565b61073181866103ed565b9450600182166000811461074c576001811461076257610795565b60ff198316865281151560200286019350610795565b61076b85610660565b60005b8381101561078d5781548189015260018201915060208101905061076e565b808801955050505b50505092915050565b600060208201905081810360008301526107b8818461071a565b905092915050565b6000815190506107cf816104af565b92915050565b6107de81610517565b81146107e957600080fd5b50565b6000815190506107fb816107d5565b92915050565b61080a816103ae565b811461081557600080fd5b50565b60008151905061082781610801565b92915050565b600080600080600060a0868803121561084957610848610494565b5b6000610857888289016107c0565b9550506020610868888289016107ec565b945050604061087988828901610818565b935050606061088a88828901610818565b925050608061089b888289016107c0565b9150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006108e282610377565b91506108ed83610377565b9250828203905060ff811115610906576109056108a8565b5b92915050565b60008160011c9050919050565b6000808291508390505b60018511156109635780860481111561093f5761093e6108a8565b5b600185161561094e5780820291505b808102905061095c8561090c565b9450610923565b94509492505050565b60008261097c5760019050610a38565b8161098a5760009050610a38565b81600181146109a057600281146109aa576109d9565b6001915050610a38565b60ff8411156109bc576109bb6108a8565b5b8360020a9150848211156109d3576109d26108a8565b5b50610a38565b5060208310610133831016604e8410600b8410161715610a0e5782820a905083811115610a0957610a086108a8565b5b610a38565b610a1b8484846001610919565b92509050818404811115610a3257610a316108a8565b5b81810290505b9392505050565b6000610a4a826103ae565b9150610a5583610377565b9250610a827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848461096c565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000610ac482610517565b9150610acf83610517565b925082610adf57610ade610a8a565b5b600160000383147f800000000000000000000000000000000000000000000000000000000000000083141615610b1857610b176108a8565b5b82820590509291505056fea2646970667358221220665d6835ef0fb5e6a6be054bbc231e9245801a249e34e054bb4b1b9bcfd25f7564736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061007d5760003560e01c80637284e4161161005b5780637284e416146100dc5780639a6fc8f5146100fa578063a8aa1b311461012e578063feaf968c1461014c5761007d565b8063313ce5671461008257806332424aa3146100a057806354fd4d50146100be575b600080fd5b61008a61016e565b6040516100979190610393565b60405180910390f35b6100a8610185565b6040516100b59190610393565b60405180910390f35b6100c6610198565b6040516100d391906103c7565b60405180910390f35b6100e46101a1565b6040516100f19190610472565b60405180910390f35b610114600480360381019061010f91906104db565b6101c9565b604051610125959493929190610530565b60405180910390f35b6101366101ec565b6040516101439190610472565b60405180910390f35b61015461027a565b604051610165959493929190610530565b60405180910390f35b6000600160009054906101000a900460ff16905090565b600160009054906101000a900460ff1681565b60006001905090565b606060006040516020016101b591906106f8565b604051602081830303815290604052905090565b60008060008060006101d961027a565b9450945094509450945091939590929450565b600080546101f99061062f565b80601f01602080910402602001604051908101604052809291908181526020018280546102259061062f565b80156102725780601f1061024757610100808354040283529160200191610272565b820191906000526020600020905b81548152906001019060200180831161025557829003601f168201915b505050505081565b60008060008060008060008060008061080173ffffffffffffffffffffffffffffffffffffffff1663ece378ed60006040518263ffffffff1660e01b81526004016102c5919061079e565b60a060405180830381865afa1580156102e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610306919061082d565b9450945094509450945061031984610336565b985084898484849950995099509950995050505050509091929394565b600080600160009054906101000a900460ff16601261035591906108d7565b905080600a6103649190610a3f565b8361036f9190610ab9565b915050919050565b600060ff82169050919050565b61038d81610377565b82525050565b60006020820190506103a86000830184610384565b92915050565b6000819050919050565b6103c1816103ae565b82525050565b60006020820190506103dc60008301846103b8565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561041c578082015181840152602081019050610401565b60008484015250505050565b6000601f19601f8301169050919050565b6000610444826103e2565b61044e81856103ed565b935061045e8185602086016103fe565b61046781610428565b840191505092915050565b6000602082019050818103600083015261048c8184610439565b905092915050565b600080fd5b600069ffffffffffffffffffff82169050919050565b6104b881610499565b81146104c357600080fd5b50565b6000813590506104d5816104af565b92915050565b6000602082840312156104f1576104f0610494565b5b60006104ff848285016104c6565b91505092915050565b61051181610499565b82525050565b6000819050919050565b61052a81610517565b82525050565b600060a0820190506105456000830188610508565b6105526020830187610521565b61055f60408301866103b8565b61056c60608301856103b8565b6105796080830184610508565b9695505050505050565b600081905092915050565b7f4e6962697275204f7261636c6520436861696e4c696e6b2d6c696b652070726960008201527f6365206665656420666f72200000000000000000000000000000000000000000602082015250565b60006105ea602c83610583565b91506105f58261058e565b602c82019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061064757607f821691505b60208210810361065a57610659610600565b5b50919050565b60008190508160005260206000209050919050565b600081546106828161062f565b61068c8186610583565b945060018216600081146106a757600181146106bc576106ef565b60ff19831686528115158202860193506106ef565b6106c585610660565b60005b838110156106e7578154818901526001820191506020810190506106c8565b838801955050505b50505092915050565b6000610703826105dd565b915061070f8284610675565b915081905092915050565b600081546107278161062f565b61073181866103ed565b9450600182166000811461074c576001811461076257610795565b60ff198316865281151560200286019350610795565b61076b85610660565b60005b8381101561078d5781548189015260018201915060208101905061076e565b808801955050505b50505092915050565b600060208201905081810360008301526107b8818461071a565b905092915050565b6000815190506107cf816104af565b92915050565b6107de81610517565b81146107e957600080fd5b50565b6000815190506107fb816107d5565b92915050565b61080a816103ae565b811461081557600080fd5b50565b60008151905061082781610801565b92915050565b600080600080600060a0868803121561084957610848610494565b5b6000610857888289016107c0565b9550506020610868888289016107ec565b945050604061087988828901610818565b935050606061088a88828901610818565b925050608061089b888289016107c0565b9150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006108e282610377565b91506108ed83610377565b9250828203905060ff811115610906576109056108a8565b5b92915050565b60008160011c9050919050565b6000808291508390505b60018511156109635780860481111561093f5761093e6108a8565b5b600185161561094e5780820291505b808102905061095c8561090c565b9450610923565b94509492505050565b60008261097c5760019050610a38565b8161098a5760009050610a38565b81600181146109a057600281146109aa576109d9565b6001915050610a38565b60ff8411156109bc576109bb6108a8565b5b8360020a9150848211156109d3576109d26108a8565b5b50610a38565b5060208310610133831016604e8410600b8410161715610a0e5782820a905083811115610a0957610a086108a8565b5b610a38565b610a1b8484846001610919565b92509050818404811115610a3257610a316108a8565b5b81810290505b9392505050565b6000610a4a826103ae565b9150610a5583610377565b9250610a827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848461096c565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000610ac482610517565b9150610acf83610517565b925082610adf57610ade610a8a565b5b600160000383147f800000000000000000000000000000000000000000000000000000000000000083141615610b1857610b176108a8565b5b82820590509291505056fea2646970667358221220665d6835ef0fb5e6a6be054bbc231e9245801a249e34e054bb4b1b9bcfd25f7564736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack4.sol/TestDirtyStateAttack4.json b/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack4.sol/TestDirtyStateAttack4.json new file mode 100644 index 000000000..3907e082f --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack4.sol/TestDirtyStateAttack4.json @@ -0,0 +1,47 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestDirtyStateAttack4", + "sourceName": "contracts/TestDirtyStateAttack4.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "wasmAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + } + ], + "name": "attack", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60806040526000805561084a806100176000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806333c889971461003b5780638ada066e14610057575b600080fd5b610055600480360381019061005091906102d6565b610075565b005b61005f6101e4565b60405161006c9190610370565b60405180910390f35b600080815480929190610087906103ba565b91905055506000600167ffffffffffffffff8111156100a9576100a8610402565b5b6040519080825280602002602001820160405280156100e257816020015b6100cf6101ed565b8152602001906001900390816100c75790505b50905060405180604001604052806040518060400160405280600581526020017f756e6962690000000000000000000000000000000000000000000000000000008152508152602001620f42408152508160008151811061014657610145610431565b5b602002602001018190525061080273ffffffffffffffffffffffffffffffffffffffff166361ffaee486868686866040518663ffffffff1660e01b8152600401610194959493929190610689565b6000604051808303816000875af11580156101b3573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906101dc91906107cb565b505050505050565b60008054905090565b604051806040016040528060608152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126102405761023f61021b565b5b8235905067ffffffffffffffff81111561025d5761025c610220565b5b60208301915083600182028301111561027957610278610225565b5b9250929050565b60008083601f8401126102965761029561021b565b5b8235905067ffffffffffffffff8111156102b3576102b2610220565b5b6020830191508360018202830111156102cf576102ce610225565b5b9250929050565b600080600080604085870312156102f0576102ef610211565b5b600085013567ffffffffffffffff81111561030e5761030d610216565b5b61031a8782880161022a565b9450945050602085013567ffffffffffffffff81111561033d5761033c610216565b5b61034987828801610280565b925092505092959194509250565b6000819050919050565b61036a81610357565b82525050565b60006020820190506103856000830184610361565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006103c582610357565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036103f7576103f661038b565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b600061049d8385610460565b93506104aa838584610471565b6104b383610480565b840190509392505050565b600082825260208201905092915050565b60006104db83856104be565b93506104e8838584610471565b6104f183610480565b840190509392505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610562578082015181840152602081019050610547565b60008484015250505050565b600061057982610528565b6105838185610533565b9350610593818560208601610544565b61059c81610480565b840191505092915050565b6105b081610357565b82525050565b600060408301600083015184820360008601526105d3828261056e565b91505060208301516105e860208601826105a7565b508091505092915050565b60006105ff83836105b6565b905092915050565b6000602082019050919050565b600061061f826104fc565b6106298185610507565b93508360208202850161063b85610518565b8060005b85811015610677578484038952815161065885826105f3565b945061066383610607565b925060208a0199505060018101905061063f565b50829750879550505050505092915050565b600060608201905081810360008301526106a4818789610491565b905081810360208301526106b98185876104cf565b905081810360408301526106cd8184610614565b90509695505050505050565b600080fd5b6106e782610480565b810181811067ffffffffffffffff8211171561070657610705610402565b5b80604052505050565b6000610719610207565b905061072582826106de565b919050565b600067ffffffffffffffff82111561074557610744610402565b5b61074e82610480565b9050602081019050919050565b600061076e6107698461072a565b61070f565b90508281526020810184848401111561078a576107896106d9565b5b610795848285610544565b509392505050565b600082601f8301126107b2576107b161021b565b5b81516107c284826020860161075b565b91505092915050565b6000602082840312156107e1576107e0610211565b5b600082015167ffffffffffffffff8111156107ff576107fe610216565b5b61080b8482850161079d565b9150509291505056fea26469706673582212208caac82ecde5bb31fb5b34bd6b569b73277f17eb9b132d65942a718f83178c9c64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806333c889971461003b5780638ada066e14610057575b600080fd5b610055600480360381019061005091906102d6565b610075565b005b61005f6101e4565b60405161006c9190610370565b60405180910390f35b600080815480929190610087906103ba565b91905055506000600167ffffffffffffffff8111156100a9576100a8610402565b5b6040519080825280602002602001820160405280156100e257816020015b6100cf6101ed565b8152602001906001900390816100c75790505b50905060405180604001604052806040518060400160405280600581526020017f756e6962690000000000000000000000000000000000000000000000000000008152508152602001620f42408152508160008151811061014657610145610431565b5b602002602001018190525061080273ffffffffffffffffffffffffffffffffffffffff166361ffaee486868686866040518663ffffffff1660e01b8152600401610194959493929190610689565b6000604051808303816000875af11580156101b3573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906101dc91906107cb565b505050505050565b60008054905090565b604051806040016040528060608152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126102405761023f61021b565b5b8235905067ffffffffffffffff81111561025d5761025c610220565b5b60208301915083600182028301111561027957610278610225565b5b9250929050565b60008083601f8401126102965761029561021b565b5b8235905067ffffffffffffffff8111156102b3576102b2610220565b5b6020830191508360018202830111156102cf576102ce610225565b5b9250929050565b600080600080604085870312156102f0576102ef610211565b5b600085013567ffffffffffffffff81111561030e5761030d610216565b5b61031a8782880161022a565b9450945050602085013567ffffffffffffffff81111561033d5761033c610216565b5b61034987828801610280565b925092505092959194509250565b6000819050919050565b61036a81610357565b82525050565b60006020820190506103856000830184610361565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006103c582610357565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036103f7576103f661038b565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b600061049d8385610460565b93506104aa838584610471565b6104b383610480565b840190509392505050565b600082825260208201905092915050565b60006104db83856104be565b93506104e8838584610471565b6104f183610480565b840190509392505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610562578082015181840152602081019050610547565b60008484015250505050565b600061057982610528565b6105838185610533565b9350610593818560208601610544565b61059c81610480565b840191505092915050565b6105b081610357565b82525050565b600060408301600083015184820360008601526105d3828261056e565b91505060208301516105e860208601826105a7565b508091505092915050565b60006105ff83836105b6565b905092915050565b6000602082019050919050565b600061061f826104fc565b6106298185610507565b93508360208202850161063b85610518565b8060005b85811015610677578484038952815161065885826105f3565b945061066383610607565b925060208a0199505060018101905061063f565b50829750879550505050505092915050565b600060608201905081810360008301526106a4818789610491565b905081810360208301526106b98185876104cf565b905081810360408301526106cd8184610614565b90509695505050505050565b600080fd5b6106e782610480565b810181811067ffffffffffffffff8211171561070657610705610402565b5b80604052505050565b6000610719610207565b905061072582826106de565b919050565b600067ffffffffffffffff82111561074557610744610402565b5b61074e82610480565b9050602081019050919050565b600061076e6107698461072a565b61070f565b90508281526020810184848401111561078a576107896106d9565b5b610795848285610544565b509392505050565b600082601f8301126107b2576107b161021b565b5b81516107c284826020860161075b565b91505092915050565b6000602082840312156107e1576107e0610211565b5b600082015167ffffffffffffffff8111156107ff576107fe610216565b5b61080b8482850161079d565b9150509291505056fea26469706673582212208caac82ecde5bb31fb5b34bd6b569b73277f17eb9b132d65942a718f83178c9c64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack5.sol/TestDirtyStateAttack5.json b/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack5.sol/TestDirtyStateAttack5.json new file mode 100644 index 000000000..80d00a824 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestDirtyStateAttack5.sol/TestDirtyStateAttack5.json @@ -0,0 +1,34 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestDirtyStateAttack5", + "sourceName": "contracts/TestDirtyStateAttack5.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "wasmAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + } + ], + "name": "attack", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x6080604052610760806100136000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806333c8899714610030575b600080fd5b61004a6004803603810190610045919061028d565b61004c565b005b6000600167ffffffffffffffff8111156100695761006861030e565b5b6040519080825280602002602001820160405280156100a257816020015b61008f6101a4565b8152602001906001900390816100875790505b50905060405180604001604052806040518060400160405280600581526020017f756e6962690000000000000000000000000000000000000000000000000000008152508152602001624c4b40815250816000815181106101065761010561033d565b5b602002602001018190525061080273ffffffffffffffffffffffffffffffffffffffff166361ffaee486868686866040518663ffffffff1660e01b815260040161015495949392919061059f565b6000604051808303816000875af1158015610173573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061019c91906106e1565b505050505050565b604051806040016040528060608152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126101f7576101f66101d2565b5b8235905067ffffffffffffffff811115610214576102136101d7565b5b6020830191508360018202830111156102305761022f6101dc565b5b9250929050565b60008083601f84011261024d5761024c6101d2565b5b8235905067ffffffffffffffff81111561026a576102696101d7565b5b602083019150836001820283011115610286576102856101dc565b5b9250929050565b600080600080604085870312156102a7576102a66101c8565b5b600085013567ffffffffffffffff8111156102c5576102c46101cd565b5b6102d1878288016101e1565b9450945050602085013567ffffffffffffffff8111156102f4576102f36101cd565b5b61030087828801610237565b925092505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006103a9838561036c565b93506103b683858461037d565b6103bf8361038c565b840190509392505050565b600082825260208201905092915050565b60006103e783856103ca565b93506103f483858461037d565b6103fd8361038c565b840190509392505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561046e578082015181840152602081019050610453565b60008484015250505050565b600061048582610434565b61048f818561043f565b935061049f818560208601610450565b6104a88161038c565b840191505092915050565b6000819050919050565b6104c6816104b3565b82525050565b600060408301600083015184820360008601526104e9828261047a565b91505060208301516104fe60208601826104bd565b508091505092915050565b600061051583836104cc565b905092915050565b6000602082019050919050565b600061053582610408565b61053f8185610413565b93508360208202850161055185610424565b8060005b8581101561058d578484038952815161056e8582610509565b94506105798361051d565b925060208a01995050600181019050610555565b50829750879550505050505092915050565b600060608201905081810360008301526105ba81878961039d565b905081810360208301526105cf8185876103db565b905081810360408301526105e3818461052a565b90509695505050505050565b600080fd5b6105fd8261038c565b810181811067ffffffffffffffff8211171561061c5761061b61030e565b5b80604052505050565b600061062f6101be565b905061063b82826105f4565b919050565b600067ffffffffffffffff82111561065b5761065a61030e565b5b6106648261038c565b9050602081019050919050565b600061068461067f84610640565b610625565b9050828152602081018484840111156106a05761069f6105ef565b5b6106ab848285610450565b509392505050565b600082601f8301126106c8576106c76101d2565b5b81516106d8848260208601610671565b91505092915050565b6000602082840312156106f7576106f66101c8565b5b600082015167ffffffffffffffff811115610715576107146101cd565b5b610721848285016106b3565b9150509291505056fea2646970667358221220035e628d5c38012c486ab00ce495fa418401de59d5a1cbfe1e48245374f7d1be64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c806333c8899714610030575b600080fd5b61004a6004803603810190610045919061028d565b61004c565b005b6000600167ffffffffffffffff8111156100695761006861030e565b5b6040519080825280602002602001820160405280156100a257816020015b61008f6101a4565b8152602001906001900390816100875790505b50905060405180604001604052806040518060400160405280600581526020017f756e6962690000000000000000000000000000000000000000000000000000008152508152602001624c4b40815250816000815181106101065761010561033d565b5b602002602001018190525061080273ffffffffffffffffffffffffffffffffffffffff166361ffaee486868686866040518663ffffffff1660e01b815260040161015495949392919061059f565b6000604051808303816000875af1158015610173573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525081019061019c91906106e1565b505050505050565b604051806040016040528060608152602001600081525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126101f7576101f66101d2565b5b8235905067ffffffffffffffff811115610214576102136101d7565b5b6020830191508360018202830111156102305761022f6101dc565b5b9250929050565b60008083601f84011261024d5761024c6101d2565b5b8235905067ffffffffffffffff81111561026a576102696101d7565b5b602083019150836001820283011115610286576102856101dc565b5b9250929050565b600080600080604085870312156102a7576102a66101c8565b5b600085013567ffffffffffffffff8111156102c5576102c46101cd565b5b6102d1878288016101e1565b9450945050602085013567ffffffffffffffff8111156102f4576102f36101cd565b5b61030087828801610237565b925092505092959194509250565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006103a9838561036c565b93506103b683858461037d565b6103bf8361038c565b840190509392505050565b600082825260208201905092915050565b60006103e783856103ca565b93506103f483858461037d565b6103fd8361038c565b840190509392505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561046e578082015181840152602081019050610453565b60008484015250505050565b600061048582610434565b61048f818561043f565b935061049f818560208601610450565b6104a88161038c565b840191505092915050565b6000819050919050565b6104c6816104b3565b82525050565b600060408301600083015184820360008601526104e9828261047a565b91505060208301516104fe60208601826104bd565b508091505092915050565b600061051583836104cc565b905092915050565b6000602082019050919050565b600061053582610408565b61053f8185610413565b93508360208202850161055185610424565b8060005b8581101561058d578484038952815161056e8582610509565b94506105798361051d565b925060208a01995050600181019050610555565b50829750879550505050505092915050565b600060608201905081810360008301526105ba81878961039d565b905081810360208301526105cf8185876103db565b905081810360408301526105e3818461052a565b90509695505050505050565b600080fd5b6105fd8261038c565b810181811067ffffffffffffffff8211171561061c5761061b61030e565b5b80604052505050565b600061062f6101be565b905061063b82826105f4565b919050565b600067ffffffffffffffff82111561065b5761065a61030e565b5b6106648261038c565b9050602081019050919050565b600061068461067f84610640565b610625565b9050828152602081018484840111156106a05761069f6105ef565b5b6106ab848285610450565b509392505050565b600082601f8301126106c8576106c76101d2565b5b81516106d8848260208601610671565b91505092915050565b6000602082840312156106f7576106f66101c8565b5b600082015167ffffffffffffffff811115610715576107146101cd565b5b610721848285016106b3565b9150509291505056fea2646970667358221220035e628d5c38012c486ab00ce495fa418401de59d5a1cbfe1e48245374f7d1be64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestERC20.sol/TestERC20.json b/x/evm/embeds/artifacts/contracts/TestERC20.sol/TestERC20.json new file mode 100644 index 000000000..a1e9d4222 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestERC20.sol/TestERC20.json @@ -0,0 +1,286 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestERC20", + "sourceName": "contracts/TestERC20.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b506040518060400160405280600981526020017f54657374455243323000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f464f4f000000000000000000000000000000000000000000000000000000000081525081600390816200008f9190620004b7565b508060049081620000a19190620004b7565b505050620000c03369d3c21bcecceda1000000620000c660201b60201c565b620006b9565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160362000138576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200012f90620005ff565b60405180910390fd5b6200014c600083836200023360201b60201c565b806002600082825462000160919062000650565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516200021391906200069c565b60405180910390a36200022f600083836200023860201b60201c565b5050565b505050565b505050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002bf57607f821691505b602082108103620002d557620002d462000277565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026200033f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000300565b6200034b868362000300565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b600062000398620003926200038c8462000363565b6200036d565b62000363565b9050919050565b6000819050919050565b620003b48362000377565b620003cc620003c3826200039f565b8484546200030d565b825550505050565b600090565b620003e3620003d4565b620003f0818484620003a9565b505050565b5b8181101562000418576200040c600082620003d9565b600181019050620003f6565b5050565b601f82111562000467576200043181620002db565b6200043c84620002f0565b810160208510156200044c578190505b620004646200045b85620002f0565b830182620003f5565b50505b505050565b600082821c905092915050565b60006200048c600019846008026200046c565b1980831691505092915050565b6000620004a7838362000479565b9150826002028217905092915050565b620004c2826200023d565b67ffffffffffffffff811115620004de57620004dd62000248565b5b620004ea8254620002a6565b620004f78282856200041c565b600060209050601f8311600181146200052f57600084156200051a578287015190505b62000526858262000499565b86555062000596565b601f1984166200053f86620002db565b60005b82811015620005695784890151825560018201915060208501945060208101905062000542565b8683101562000589578489015162000585601f89168262000479565b8355505b6001600288020188555050505b505050505050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000620005e7601f836200059e565b9150620005f482620005af565b602082019050919050565b600060208201905081810360008301526200061a81620005d8565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006200065d8262000363565b91506200066a8362000363565b925082820190508082111562000685576200068462000621565b5b92915050565b620006968162000363565b82525050565b6000602082019050620006b360008301846200068b565b92915050565b61122f80620006c96000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b0c565b60405180910390f35b6100e660048036038101906100e19190610bc7565b610308565b6040516100f39190610c22565b60405180910390f35b61010461032b565b6040516101119190610c4c565b60405180910390f35b610134600480360381019061012f9190610c67565b610335565b6040516101419190610c22565b60405180910390f35b610152610364565b60405161015f9190610cd6565b60405180910390f35b610182600480360381019061017d9190610bc7565b61036d565b60405161018f9190610c22565b60405180910390f35b6101b260048036038101906101ad9190610cf1565b6103a4565b6040516101bf9190610c4c565b60405180910390f35b6101d06103ec565b6040516101dd9190610b0c565b60405180910390f35b61020060048036038101906101fb9190610bc7565b61047e565b60405161020d9190610c22565b60405180910390f35b610230600480360381019061022b9190610bc7565b6104f5565b60405161023d9190610c22565b60405180910390f35b610260600480360381019061025b9190610d1e565b610518565b60405161026d9190610c4c565b60405180910390f35b60606003805461028590610d8d565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190610d8d565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b60008061031361059f565b90506103208185856105a7565b600191505092915050565b6000600254905090565b60008061034061059f565b905061034d858285610770565b6103588585856107fc565b60019150509392505050565b60006012905090565b60008061037861059f565b905061039981858561038a8589610518565b6103949190610ded565b6105a7565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060600480546103fb90610d8d565b80601f016020809104026020016040519081016040528092919081815260200182805461042790610d8d565b80156104745780601f1061044957610100808354040283529160200191610474565b820191906000526020600020905b81548152906001019060200180831161045757829003601f168201915b5050505050905090565b60008061048961059f565b905060006104978286610518565b9050838110156104dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104d390610e93565b60405180910390fd5b6104e982868684036105a7565b60019250505092915050565b60008061050061059f565b905061050d8185856107fc565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610616576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060d90610f25565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067c90610fb7565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516107639190610c4c565b60405180910390a3505050565b600061077c8484610518565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146107f657818110156107e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107df90611023565b60405180910390fd5b6107f584848484036105a7565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361086b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610862906110b5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d190611147565b60405180910390fd5b6108e5838383610a72565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561096b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610962906111d9565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610a599190610c4c565b60405180910390a3610a6c848484610a77565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ab6578082015181840152602081019050610a9b565b60008484015250505050565b6000601f19601f8301169050919050565b6000610ade82610a7c565b610ae88185610a87565b9350610af8818560208601610a98565b610b0181610ac2565b840191505092915050565b60006020820190508181036000830152610b268184610ad3565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610b5e82610b33565b9050919050565b610b6e81610b53565b8114610b7957600080fd5b50565b600081359050610b8b81610b65565b92915050565b6000819050919050565b610ba481610b91565b8114610baf57600080fd5b50565b600081359050610bc181610b9b565b92915050565b60008060408385031215610bde57610bdd610b2e565b5b6000610bec85828601610b7c565b9250506020610bfd85828601610bb2565b9150509250929050565b60008115159050919050565b610c1c81610c07565b82525050565b6000602082019050610c376000830184610c13565b92915050565b610c4681610b91565b82525050565b6000602082019050610c616000830184610c3d565b92915050565b600080600060608486031215610c8057610c7f610b2e565b5b6000610c8e86828701610b7c565b9350506020610c9f86828701610b7c565b9250506040610cb086828701610bb2565b9150509250925092565b600060ff82169050919050565b610cd081610cba565b82525050565b6000602082019050610ceb6000830184610cc7565b92915050565b600060208284031215610d0757610d06610b2e565b5b6000610d1584828501610b7c565b91505092915050565b60008060408385031215610d3557610d34610b2e565b5b6000610d4385828601610b7c565b9250506020610d5485828601610b7c565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610da557607f821691505b602082108103610db857610db7610d5e565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610df882610b91565b9150610e0383610b91565b9250828201905080821115610e1b57610e1a610dbe565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610e7d602583610a87565b9150610e8882610e21565b604082019050919050565b60006020820190508181036000830152610eac81610e70565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000610f0f602483610a87565b9150610f1a82610eb3565b604082019050919050565b60006020820190508181036000830152610f3e81610f02565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000610fa1602283610a87565b9150610fac82610f45565b604082019050919050565b60006020820190508181036000830152610fd081610f94565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061100d601d83610a87565b915061101882610fd7565b602082019050919050565b6000602082019050818103600083015261103c81611000565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b600061109f602583610a87565b91506110aa82611043565b604082019050919050565b600060208201905081810360008301526110ce81611092565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611131602383610a87565b915061113c826110d5565b604082019050919050565b6000602082019050818103600083015261116081611124565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b60006111c3602683610a87565b91506111ce82611167565b604082019050919050565b600060208201905081810360008301526111f2816111b6565b905091905056fea26469706673582212209daf0c2b0a4ff52e932f2c8ac4b752a1b3d828de9c9661a370cb5d05214e85b664736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b0c565b60405180910390f35b6100e660048036038101906100e19190610bc7565b610308565b6040516100f39190610c22565b60405180910390f35b61010461032b565b6040516101119190610c4c565b60405180910390f35b610134600480360381019061012f9190610c67565b610335565b6040516101419190610c22565b60405180910390f35b610152610364565b60405161015f9190610cd6565b60405180910390f35b610182600480360381019061017d9190610bc7565b61036d565b60405161018f9190610c22565b60405180910390f35b6101b260048036038101906101ad9190610cf1565b6103a4565b6040516101bf9190610c4c565b60405180910390f35b6101d06103ec565b6040516101dd9190610b0c565b60405180910390f35b61020060048036038101906101fb9190610bc7565b61047e565b60405161020d9190610c22565b60405180910390f35b610230600480360381019061022b9190610bc7565b6104f5565b60405161023d9190610c22565b60405180910390f35b610260600480360381019061025b9190610d1e565b610518565b60405161026d9190610c4c565b60405180910390f35b60606003805461028590610d8d565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190610d8d565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b60008061031361059f565b90506103208185856105a7565b600191505092915050565b6000600254905090565b60008061034061059f565b905061034d858285610770565b6103588585856107fc565b60019150509392505050565b60006012905090565b60008061037861059f565b905061039981858561038a8589610518565b6103949190610ded565b6105a7565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060600480546103fb90610d8d565b80601f016020809104026020016040519081016040528092919081815260200182805461042790610d8d565b80156104745780601f1061044957610100808354040283529160200191610474565b820191906000526020600020905b81548152906001019060200180831161045757829003601f168201915b5050505050905090565b60008061048961059f565b905060006104978286610518565b9050838110156104dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104d390610e93565b60405180910390fd5b6104e982868684036105a7565b60019250505092915050565b60008061050061059f565b905061050d8185856107fc565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610616576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060d90610f25565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067c90610fb7565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516107639190610c4c565b60405180910390a3505050565b600061077c8484610518565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146107f657818110156107e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107df90611023565b60405180910390fd5b6107f584848484036105a7565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361086b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610862906110b5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d190611147565b60405180910390fd5b6108e5838383610a72565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561096b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610962906111d9565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610a599190610c4c565b60405180910390a3610a6c848484610a77565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ab6578082015181840152602081019050610a9b565b60008484015250505050565b6000601f19601f8301169050919050565b6000610ade82610a7c565b610ae88185610a87565b9350610af8818560208601610a98565b610b0181610ac2565b840191505092915050565b60006020820190508181036000830152610b268184610ad3565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610b5e82610b33565b9050919050565b610b6e81610b53565b8114610b7957600080fd5b50565b600081359050610b8b81610b65565b92915050565b6000819050919050565b610ba481610b91565b8114610baf57600080fd5b50565b600081359050610bc181610b9b565b92915050565b60008060408385031215610bde57610bdd610b2e565b5b6000610bec85828601610b7c565b9250506020610bfd85828601610bb2565b9150509250929050565b60008115159050919050565b610c1c81610c07565b82525050565b6000602082019050610c376000830184610c13565b92915050565b610c4681610b91565b82525050565b6000602082019050610c616000830184610c3d565b92915050565b600080600060608486031215610c8057610c7f610b2e565b5b6000610c8e86828701610b7c565b9350506020610c9f86828701610b7c565b9250506040610cb086828701610bb2565b9150509250925092565b600060ff82169050919050565b610cd081610cba565b82525050565b6000602082019050610ceb6000830184610cc7565b92915050565b600060208284031215610d0757610d06610b2e565b5b6000610d1584828501610b7c565b91505092915050565b60008060408385031215610d3557610d34610b2e565b5b6000610d4385828601610b7c565b9250506020610d5485828601610b7c565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610da557607f821691505b602082108103610db857610db7610d5e565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610df882610b91565b9150610e0383610b91565b9250828201905080821115610e1b57610e1a610dbe565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610e7d602583610a87565b9150610e8882610e21565b604082019050919050565b60006020820190508181036000830152610eac81610e70565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000610f0f602483610a87565b9150610f1a82610eb3565b604082019050919050565b60006020820190508181036000830152610f3e81610f02565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000610fa1602283610a87565b9150610fac82610f45565b604082019050919050565b60006020820190508181036000830152610fd081610f94565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061100d601d83610a87565b915061101882610fd7565b602082019050919050565b6000602082019050818103600083015261103c81611000565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b600061109f602583610a87565b91506110aa82611043565b604082019050919050565b600060208201905081810360008301526110ce81611092565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611131602383610a87565b915061113c826110d5565b604082019050919050565b6000602082019050818103600083015261116081611124565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b60006111c3602683610a87565b91506111ce82611167565b604082019050919050565b600060208201905081810360008301526111f2816111b6565b905091905056fea26469706673582212209daf0c2b0a4ff52e932f2c8ac4b752a1b3d828de9c9661a370cb5d05214e85b664736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestERC20MaliciousName.sol/TestERC20MaliciousName.json b/x/evm/embeds/artifacts/contracts/TestERC20MaliciousName.sol/TestERC20MaliciousName.json new file mode 100644 index 000000000..766929c8f --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestERC20MaliciousName.sol/TestERC20MaliciousName.json @@ -0,0 +1,302 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestERC20MaliciousName", + "sourceName": "contracts/TestERC20MaliciousName.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162001c5638038062001c568339818101604052810190620000379190620003cc565b828281600390816200004a9190620006b1565b5080600490816200005c9190620006b1565b5050506200007b3369d3c21bcecceda10000006200008460201b60201c565b505050620008b3565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603620000f6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000ed90620007f9565b60405180910390fd5b6200010a60008383620001f160201b60201c565b80600260008282546200011e91906200084a565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620001d1919062000896565b60405180910390a3620001ed60008383620001f660201b60201c565b5050565b505050565b505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620002648262000219565b810181811067ffffffffffffffff821117156200028657620002856200022a565b5b80604052505050565b60006200029b620001fb565b9050620002a9828262000259565b919050565b600067ffffffffffffffff821115620002cc57620002cb6200022a565b5b620002d78262000219565b9050602081019050919050565b60005b8381101562000304578082015181840152602081019050620002e7565b60008484015250505050565b6000620003276200032184620002ae565b6200028f565b90508281526020810184848401111562000346576200034562000214565b5b62000353848285620002e4565b509392505050565b600082601f8301126200037357620003726200020f565b5b81516200038584826020860162000310565b91505092915050565b600060ff82169050919050565b620003a6816200038e565b8114620003b257600080fd5b50565b600081519050620003c6816200039b565b92915050565b600080600060608486031215620003e857620003e762000205565b5b600084015167ffffffffffffffff8111156200040957620004086200020a565b5b62000417868287016200035b565b935050602084015167ffffffffffffffff8111156200043b576200043a6200020a565b5b62000449868287016200035b565b92505060406200045c86828701620003b5565b9150509250925092565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620004b957607f821691505b602082108103620004cf57620004ce62000471565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620005397fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620004fa565b620005458683620004fa565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620005926200058c62000586846200055d565b62000567565b6200055d565b9050919050565b6000819050919050565b620005ae8362000571565b620005c6620005bd8262000599565b84845462000507565b825550505050565b600090565b620005dd620005ce565b620005ea818484620005a3565b505050565b5b81811015620006125762000606600082620005d3565b600181019050620005f0565b5050565b601f82111562000661576200062b81620004d5565b6200063684620004ea565b8101602085101562000646578190505b6200065e6200065585620004ea565b830182620005ef565b50505b505050565b600082821c905092915050565b6000620006866000198460080262000666565b1980831691505092915050565b6000620006a1838362000673565b9150826002028217905092915050565b620006bc8262000466565b67ffffffffffffffff811115620006d857620006d76200022a565b5b620006e48254620004a0565b620006f182828562000616565b600060209050601f83116001811462000729576000841562000714578287015190505b62000720858262000693565b86555062000790565b601f1984166200073986620004d5565b60005b8281101562000763578489015182556001820191506020850194506020810190506200073c565b868310156200078357848901516200077f601f89168262000673565b8355505b6001600288020188555050505b505050505050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000620007e1601f8362000798565b9150620007ee82620007a9565b602082019050919050565b600060208201905081810360008301526200081481620007d2565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600062000857826200055d565b915062000864836200055d565b92508282019050808211156200087f576200087e6200081b565b5b92915050565b62000890816200055d565b82525050565b6000602082019050620008ad600083018462000885565b92915050565b61139380620008c36000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b9f565b60405180910390f35b6100e660048036038101906100e19190610c5a565b610293565b6040516100f39190610cb5565b60405180910390f35b6101046102b6565b6040516101119190610cdf565b60405180910390f35b610134600480360381019061012f9190610cfa565b6102c0565b6040516101419190610cb5565b60405180910390f35b6101526102ef565b60405161015f9190610d69565b60405180910390f35b610182600480360381019061017d9190610c5a565b6102f8565b60405161018f9190610cb5565b60405180910390f35b6101b260048036038101906101ad9190610d84565b61032f565b6040516101bf9190610cdf565b60405180910390f35b6101d0610377565b6040516101dd9190610b9f565b60405180910390f35b61020060048036038101906101fb9190610c5a565b610409565b60405161020d9190610cb5565b60405180910390f35b610230600480360381019061022b9190610c5a565b610480565b60405161023d9190610cb5565b60405180910390f35b610260600480360381019061025b9190610db1565b6104a3565b60405161026d9190610cdf565b60405180910390f35b6060600061028261052a565b905061028c6105bc565b8091505090565b60008061029e610632565b90506102ab81858561063a565b600191505092915050565b6000600254905090565b6000806102cb610632565b90506102d8858285610803565b6102e385858561088f565b60019150509392505050565b60006012905090565b600080610303610632565b905061032481858561031585896104a3565b61031f9190610e20565b61063a565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606004805461038690610e83565b80601f01602080910402602001604051908101604052809291908181526020018280546103b290610e83565b80156103ff5780601f106103d4576101008083540402835291602001916103ff565b820191906000526020600020905b8154815290600101906020018083116103e257829003601f168201915b5050505050905090565b600080610414610632565b9050600061042282866104a3565b905083811015610467576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045e90610f26565b60405180910390fd5b610474828686840361063a565b60019250505092915050565b60008061048b610632565b905061049881858561088f565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b60606003805461053990610e83565b80601f016020809104026020016040519081016040528092919081815260200182805461056590610e83565b80156105b25780601f10610587576101008083540402835291602001916105b2565b820191906000526020600020905b81548152906001019060200180831161059557829003601f168201915b5050505050905090565b60006001905060005b620186a081101561061d5760016002836105df9190610f46565b6105e99190610e20565b91506002826105f89190610fb7565b9150600182901b8218915067ffffffffffffffff8216915080806001019150506105c5565b506000810361062f5761062e610fe8565b5b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036106a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106a090611089565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610718576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161070f9061111b565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516107f69190610cdf565b60405180910390a3505050565b600061080f84846104a3565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610889578181101561087b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161087290611187565b60405180910390fd5b610888848484840361063a565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108f590611219565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361096d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610964906112ab565b60405180910390fd5b610978838383610b05565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156109fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109f59061133d565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610aec9190610cdf565b60405180910390a3610aff848484610b0a565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b49578082015181840152602081019050610b2e565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b7182610b0f565b610b7b8185610b1a565b9350610b8b818560208601610b2b565b610b9481610b55565b840191505092915050565b60006020820190508181036000830152610bb98184610b66565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bf182610bc6565b9050919050565b610c0181610be6565b8114610c0c57600080fd5b50565b600081359050610c1e81610bf8565b92915050565b6000819050919050565b610c3781610c24565b8114610c4257600080fd5b50565b600081359050610c5481610c2e565b92915050565b60008060408385031215610c7157610c70610bc1565b5b6000610c7f85828601610c0f565b9250506020610c9085828601610c45565b9150509250929050565b60008115159050919050565b610caf81610c9a565b82525050565b6000602082019050610cca6000830184610ca6565b92915050565b610cd981610c24565b82525050565b6000602082019050610cf46000830184610cd0565b92915050565b600080600060608486031215610d1357610d12610bc1565b5b6000610d2186828701610c0f565b9350506020610d3286828701610c0f565b9250506040610d4386828701610c45565b9150509250925092565b600060ff82169050919050565b610d6381610d4d565b82525050565b6000602082019050610d7e6000830184610d5a565b92915050565b600060208284031215610d9a57610d99610bc1565b5b6000610da884828501610c0f565b91505092915050565b60008060408385031215610dc857610dc7610bc1565b5b6000610dd685828601610c0f565b9250506020610de785828601610c0f565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e2b82610c24565b9150610e3683610c24565b9250828201905080821115610e4e57610e4d610df1565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610e9b57607f821691505b602082108103610eae57610ead610e54565b5b50919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610f10602583610b1a565b9150610f1b82610eb4565b604082019050919050565b60006020820190508181036000830152610f3f81610f03565b9050919050565b6000610f5182610c24565b9150610f5c83610c24565b9250828202610f6a81610c24565b91508282048414831517610f8157610f80610df1565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000610fc282610c24565b9150610fcd83610c24565b925082610fdd57610fdc610f88565b5b828204905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000611073602483610b1a565b915061107e82611017565b604082019050919050565b600060208201905081810360008301526110a281611066565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000611105602283610b1a565b9150611110826110a9565b604082019050919050565b60006020820190508181036000830152611134816110f8565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b6000611171601d83610b1a565b915061117c8261113b565b602082019050919050565b600060208201905081810360008301526111a081611164565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000611203602583610b1a565b915061120e826111a7565b604082019050919050565b60006020820190508181036000830152611232816111f6565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611295602383610b1a565b91506112a082611239565b604082019050919050565b600060208201905081810360008301526112c481611288565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000611327602683610b1a565b9150611332826112cb565b604082019050919050565b600060208201905081810360008301526113568161131a565b905091905056fea26469706673582212205d0c992a81a43cd8431857189c1e4ae58ba867bc7888b0cc0c2e0031d6d9f3c664736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b9f565b60405180910390f35b6100e660048036038101906100e19190610c5a565b610293565b6040516100f39190610cb5565b60405180910390f35b6101046102b6565b6040516101119190610cdf565b60405180910390f35b610134600480360381019061012f9190610cfa565b6102c0565b6040516101419190610cb5565b60405180910390f35b6101526102ef565b60405161015f9190610d69565b60405180910390f35b610182600480360381019061017d9190610c5a565b6102f8565b60405161018f9190610cb5565b60405180910390f35b6101b260048036038101906101ad9190610d84565b61032f565b6040516101bf9190610cdf565b60405180910390f35b6101d0610377565b6040516101dd9190610b9f565b60405180910390f35b61020060048036038101906101fb9190610c5a565b610409565b60405161020d9190610cb5565b60405180910390f35b610230600480360381019061022b9190610c5a565b610480565b60405161023d9190610cb5565b60405180910390f35b610260600480360381019061025b9190610db1565b6104a3565b60405161026d9190610cdf565b60405180910390f35b6060600061028261052a565b905061028c6105bc565b8091505090565b60008061029e610632565b90506102ab81858561063a565b600191505092915050565b6000600254905090565b6000806102cb610632565b90506102d8858285610803565b6102e385858561088f565b60019150509392505050565b60006012905090565b600080610303610632565b905061032481858561031585896104a3565b61031f9190610e20565b61063a565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606004805461038690610e83565b80601f01602080910402602001604051908101604052809291908181526020018280546103b290610e83565b80156103ff5780601f106103d4576101008083540402835291602001916103ff565b820191906000526020600020905b8154815290600101906020018083116103e257829003601f168201915b5050505050905090565b600080610414610632565b9050600061042282866104a3565b905083811015610467576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045e90610f26565b60405180910390fd5b610474828686840361063a565b60019250505092915050565b60008061048b610632565b905061049881858561088f565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b60606003805461053990610e83565b80601f016020809104026020016040519081016040528092919081815260200182805461056590610e83565b80156105b25780601f10610587576101008083540402835291602001916105b2565b820191906000526020600020905b81548152906001019060200180831161059557829003601f168201915b5050505050905090565b60006001905060005b620186a081101561061d5760016002836105df9190610f46565b6105e99190610e20565b91506002826105f89190610fb7565b9150600182901b8218915067ffffffffffffffff8216915080806001019150506105c5565b506000810361062f5761062e610fe8565b5b50565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036106a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106a090611089565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610718576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161070f9061111b565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516107f69190610cdf565b60405180910390a3505050565b600061080f84846104a3565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610889578181101561087b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161087290611187565b60405180910390fd5b610888848484840361063a565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108f590611219565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361096d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610964906112ab565b60405180910390fd5b610978838383610b05565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156109fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109f59061133d565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610aec9190610cdf565b60405180910390a3610aff848484610b0a565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b49578082015181840152602081019050610b2e565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b7182610b0f565b610b7b8185610b1a565b9350610b8b818560208601610b2b565b610b9481610b55565b840191505092915050565b60006020820190508181036000830152610bb98184610b66565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bf182610bc6565b9050919050565b610c0181610be6565b8114610c0c57600080fd5b50565b600081359050610c1e81610bf8565b92915050565b6000819050919050565b610c3781610c24565b8114610c4257600080fd5b50565b600081359050610c5481610c2e565b92915050565b60008060408385031215610c7157610c70610bc1565b5b6000610c7f85828601610c0f565b9250506020610c9085828601610c45565b9150509250929050565b60008115159050919050565b610caf81610c9a565b82525050565b6000602082019050610cca6000830184610ca6565b92915050565b610cd981610c24565b82525050565b6000602082019050610cf46000830184610cd0565b92915050565b600080600060608486031215610d1357610d12610bc1565b5b6000610d2186828701610c0f565b9350506020610d3286828701610c0f565b9250506040610d4386828701610c45565b9150509250925092565b600060ff82169050919050565b610d6381610d4d565b82525050565b6000602082019050610d7e6000830184610d5a565b92915050565b600060208284031215610d9a57610d99610bc1565b5b6000610da884828501610c0f565b91505092915050565b60008060408385031215610dc857610dc7610bc1565b5b6000610dd685828601610c0f565b9250506020610de785828601610c0f565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e2b82610c24565b9150610e3683610c24565b9250828201905080821115610e4e57610e4d610df1565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610e9b57607f821691505b602082108103610eae57610ead610e54565b5b50919050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610f10602583610b1a565b9150610f1b82610eb4565b604082019050919050565b60006020820190508181036000830152610f3f81610f03565b9050919050565b6000610f5182610c24565b9150610f5c83610c24565b9250828202610f6a81610c24565b91508282048414831517610f8157610f80610df1565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000610fc282610c24565b9150610fcd83610c24565b925082610fdd57610fdc610f88565b5b828204905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000611073602483610b1a565b915061107e82611017565b604082019050919050565b600060208201905081810360008301526110a281611066565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000611105602283610b1a565b9150611110826110a9565b604082019050919050565b60006020820190508181036000830152611134816110f8565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b6000611171601d83610b1a565b915061117c8261113b565b602082019050919050565b600060208201905081810360008301526111a081611164565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000611203602583610b1a565b915061120e826111a7565b604082019050919050565b60006020820190508181036000830152611232816111f6565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611295602383610b1a565b91506112a082611239565b604082019050919050565b600060208201905081810360008301526112c481611288565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000611327602683610b1a565b9150611332826112cb565b604082019050919050565b600060208201905081810360008301526113568161131a565b905091905056fea26469706673582212205d0c992a81a43cd8431857189c1e4ae58ba867bc7888b0cc0c2e0031d6d9f3c664736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestERC20MaliciousTransfer.sol/TestERC20MaliciousTransfer.json b/x/evm/embeds/artifacts/contracts/TestERC20MaliciousTransfer.sol/TestERC20MaliciousTransfer.json new file mode 100644 index 000000000..48019e35d --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestERC20MaliciousTransfer.sol/TestERC20MaliciousTransfer.json @@ -0,0 +1,302 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestERC20MaliciousTransfer", + "sourceName": "contracts/TestERC20MaliciousTransfer.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162001c5538038062001c558339818101604052810190620000379190620003cc565b828281600390816200004a9190620006b1565b5080600490816200005c9190620006b1565b5050506200007b3369d3c21bcecceda10000006200008460201b60201c565b505050620008b3565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603620000f6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000ed90620007f9565b60405180910390fd5b6200010a60008383620001f160201b60201c565b80600260008282546200011e91906200084a565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620001d1919062000896565b60405180910390a3620001ed60008383620001f660201b60201c565b5050565b505050565b505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620002648262000219565b810181811067ffffffffffffffff821117156200028657620002856200022a565b5b80604052505050565b60006200029b620001fb565b9050620002a9828262000259565b919050565b600067ffffffffffffffff821115620002cc57620002cb6200022a565b5b620002d78262000219565b9050602081019050919050565b60005b8381101562000304578082015181840152602081019050620002e7565b60008484015250505050565b6000620003276200032184620002ae565b6200028f565b90508281526020810184848401111562000346576200034562000214565b5b62000353848285620002e4565b509392505050565b600082601f8301126200037357620003726200020f565b5b81516200038584826020860162000310565b91505092915050565b600060ff82169050919050565b620003a6816200038e565b8114620003b257600080fd5b50565b600081519050620003c6816200039b565b92915050565b600080600060608486031215620003e857620003e762000205565b5b600084015167ffffffffffffffff8111156200040957620004086200020a565b5b62000417868287016200035b565b935050602084015167ffffffffffffffff8111156200043b576200043a6200020a565b5b62000449868287016200035b565b92505060406200045c86828701620003b5565b9150509250925092565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620004b957607f821691505b602082108103620004cf57620004ce62000471565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620005397fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620004fa565b620005458683620004fa565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620005926200058c62000586846200055d565b62000567565b6200055d565b9050919050565b6000819050919050565b620005ae8362000571565b620005c6620005bd8262000599565b84845462000507565b825550505050565b600090565b620005dd620005ce565b620005ea818484620005a3565b505050565b5b81811015620006125762000606600082620005d3565b600181019050620005f0565b5050565b601f82111562000661576200062b81620004d5565b6200063684620004ea565b8101602085101562000646578190505b6200065e6200065585620004ea565b830182620005ef565b50505b505050565b600082821c905092915050565b6000620006866000198460080262000666565b1980831691505092915050565b6000620006a1838362000673565b9150826002028217905092915050565b620006bc8262000466565b67ffffffffffffffff811115620006d857620006d76200022a565b5b620006e48254620004a0565b620006f182828562000616565b600060209050601f83116001811462000729576000841562000714578287015190505b62000720858262000693565b86555062000790565b601f1984166200073986620004d5565b60005b8281101562000763578489015182556001820191506020850194506020810190506200073c565b868310156200078357848901516200077f601f89168262000673565b8355505b6001600288020188555050505b505050505050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000620007e1601f8362000798565b9150620007ee82620007a9565b602082019050919050565b600060208201905081810360008301526200081481620007d2565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600062000857826200055d565b915062000864836200055d565b92508282019050808211156200087f576200087e6200081b565b5b92915050565b62000890816200055d565b82525050565b6000602082019050620008ad600083018462000885565b92915050565b61139280620008c36000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b9e565b60405180910390f35b6100e660048036038101906100e19190610c59565b610308565b6040516100f39190610cb4565b60405180910390f35b61010461032b565b6040516101119190610cde565b60405180910390f35b610134600480360381019061012f9190610cf9565b610335565b6040516101419190610cb4565b60405180910390f35b610152610364565b60405161015f9190610d68565b60405180910390f35b610182600480360381019061017d9190610c59565b61036d565b60405161018f9190610cb4565b60405180910390f35b6101b260048036038101906101ad9190610d83565b6103a4565b6040516101bf9190610cde565b60405180910390f35b6101d06103ec565b6040516101dd9190610b9e565b60405180910390f35b61020060048036038101906101fb9190610c59565b61047e565b60405161020d9190610cb4565b60405180910390f35b610230600480360381019061022b9190610c59565b6104f5565b60405161023d9190610cb4565b60405180910390f35b610260600480360381019061025b9190610db0565b610511565b60405161026d9190610cde565b60405180910390f35b60606003805461028590610e1f565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190610e1f565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b600080610313610598565b90506103208185856105a0565b600191505092915050565b6000600254905090565b600080610340610598565b905061034d858285610769565b6103588585856107f5565b60019150509392505050565b60006012905090565b600080610378610598565b905061039981858561038a8589610511565b6103949190610e7f565b6105a0565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060600480546103fb90610e1f565b80601f016020809104026020016040519081016040528092919081815260200182805461042790610e1f565b80156104745780601f1061044957610100808354040283529160200191610474565b820191906000526020600020905b81548152906001019060200180831161045757829003601f168201915b5050505050905090565b600080610489610598565b905060006104978286610511565b9050838110156104dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104d390610f25565b60405180910390fd5b6104e982868684036105a0565b60019250505092915050565b60006104ff610a6b565b6105098383610ae1565b905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361060f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060690610fb7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361067e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067590611049565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405161075c9190610cde565b60405180910390a3505050565b60006107758484610511565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146107ef57818110156107e1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107d8906110b5565b60405180910390fd5b6107ee84848484036105a0565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610864576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085b90611147565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108ca906111d9565b60405180910390fd5b6108de838383610b04565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610964576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161095b9061126b565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610a529190610cde565b60405180910390a3610a65848484610b09565b50505050565b60006001905060005b620186a0811015610acc576001600283610a8e919061128b565b610a989190610e7f565b9150600282610aa791906112fc565b9150600182901b8218915067ffffffffffffffff821691508080600101915050610a74565b5060008103610ade57610add61132d565b5b50565b600080610aec610598565b9050610af98185856107f5565b600191505092915050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b48578082015181840152602081019050610b2d565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b7082610b0e565b610b7a8185610b19565b9350610b8a818560208601610b2a565b610b9381610b54565b840191505092915050565b60006020820190508181036000830152610bb88184610b65565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bf082610bc5565b9050919050565b610c0081610be5565b8114610c0b57600080fd5b50565b600081359050610c1d81610bf7565b92915050565b6000819050919050565b610c3681610c23565b8114610c4157600080fd5b50565b600081359050610c5381610c2d565b92915050565b60008060408385031215610c7057610c6f610bc0565b5b6000610c7e85828601610c0e565b9250506020610c8f85828601610c44565b9150509250929050565b60008115159050919050565b610cae81610c99565b82525050565b6000602082019050610cc96000830184610ca5565b92915050565b610cd881610c23565b82525050565b6000602082019050610cf36000830184610ccf565b92915050565b600080600060608486031215610d1257610d11610bc0565b5b6000610d2086828701610c0e565b9350506020610d3186828701610c0e565b9250506040610d4286828701610c44565b9150509250925092565b600060ff82169050919050565b610d6281610d4c565b82525050565b6000602082019050610d7d6000830184610d59565b92915050565b600060208284031215610d9957610d98610bc0565b5b6000610da784828501610c0e565b91505092915050565b60008060408385031215610dc757610dc6610bc0565b5b6000610dd585828601610c0e565b9250506020610de685828601610c0e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610e3757607f821691505b602082108103610e4a57610e49610df0565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e8a82610c23565b9150610e9583610c23565b9250828201905080821115610ead57610eac610e50565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610f0f602583610b19565b9150610f1a82610eb3565b604082019050919050565b60006020820190508181036000830152610f3e81610f02565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000610fa1602483610b19565b9150610fac82610f45565b604082019050919050565b60006020820190508181036000830152610fd081610f94565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000611033602283610b19565b915061103e82610fd7565b604082019050919050565b6000602082019050818103600083015261106281611026565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061109f601d83610b19565b91506110aa82611069565b602082019050919050565b600060208201905081810360008301526110ce81611092565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000611131602583610b19565b915061113c826110d5565b604082019050919050565b6000602082019050818103600083015261116081611124565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b60006111c3602383610b19565b91506111ce82611167565b604082019050919050565b600060208201905081810360008301526111f2816111b6565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000611255602683610b19565b9150611260826111f9565b604082019050919050565b6000602082019050818103600083015261128481611248565b9050919050565b600061129682610c23565b91506112a183610c23565b92508282026112af81610c23565b915082820484148315176112c6576112c5610e50565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061130782610c23565b915061131283610c23565b925082611322576113216112cd565b5b828204905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fdfea2646970667358221220e34d9015349ff6af20fee44587dc5ba21b370ca2a6239a1dc6440ecaddfd47a364736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b9e565b60405180910390f35b6100e660048036038101906100e19190610c59565b610308565b6040516100f39190610cb4565b60405180910390f35b61010461032b565b6040516101119190610cde565b60405180910390f35b610134600480360381019061012f9190610cf9565b610335565b6040516101419190610cb4565b60405180910390f35b610152610364565b60405161015f9190610d68565b60405180910390f35b610182600480360381019061017d9190610c59565b61036d565b60405161018f9190610cb4565b60405180910390f35b6101b260048036038101906101ad9190610d83565b6103a4565b6040516101bf9190610cde565b60405180910390f35b6101d06103ec565b6040516101dd9190610b9e565b60405180910390f35b61020060048036038101906101fb9190610c59565b61047e565b60405161020d9190610cb4565b60405180910390f35b610230600480360381019061022b9190610c59565b6104f5565b60405161023d9190610cb4565b60405180910390f35b610260600480360381019061025b9190610db0565b610511565b60405161026d9190610cde565b60405180910390f35b60606003805461028590610e1f565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190610e1f565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b600080610313610598565b90506103208185856105a0565b600191505092915050565b6000600254905090565b600080610340610598565b905061034d858285610769565b6103588585856107f5565b60019150509392505050565b60006012905090565b600080610378610598565b905061039981858561038a8589610511565b6103949190610e7f565b6105a0565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060600480546103fb90610e1f565b80601f016020809104026020016040519081016040528092919081815260200182805461042790610e1f565b80156104745780601f1061044957610100808354040283529160200191610474565b820191906000526020600020905b81548152906001019060200180831161045757829003601f168201915b5050505050905090565b600080610489610598565b905060006104978286610511565b9050838110156104dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104d390610f25565b60405180910390fd5b6104e982868684036105a0565b60019250505092915050565b60006104ff610a6b565b6105098383610ae1565b905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361060f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060690610fb7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361067e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067590611049565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405161075c9190610cde565b60405180910390a3505050565b60006107758484610511565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146107ef57818110156107e1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107d8906110b5565b60405180910390fd5b6107ee84848484036105a0565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610864576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085b90611147565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108ca906111d9565b60405180910390fd5b6108de838383610b04565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610964576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161095b9061126b565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610a529190610cde565b60405180910390a3610a65848484610b09565b50505050565b60006001905060005b620186a0811015610acc576001600283610a8e919061128b565b610a989190610e7f565b9150600282610aa791906112fc565b9150600182901b8218915067ffffffffffffffff821691508080600101915050610a74565b5060008103610ade57610add61132d565b5b50565b600080610aec610598565b9050610af98185856107f5565b600191505092915050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b48578082015181840152602081019050610b2d565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b7082610b0e565b610b7a8185610b19565b9350610b8a818560208601610b2a565b610b9381610b54565b840191505092915050565b60006020820190508181036000830152610bb88184610b65565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bf082610bc5565b9050919050565b610c0081610be5565b8114610c0b57600080fd5b50565b600081359050610c1d81610bf7565b92915050565b6000819050919050565b610c3681610c23565b8114610c4157600080fd5b50565b600081359050610c5381610c2d565b92915050565b60008060408385031215610c7057610c6f610bc0565b5b6000610c7e85828601610c0e565b9250506020610c8f85828601610c44565b9150509250929050565b60008115159050919050565b610cae81610c99565b82525050565b6000602082019050610cc96000830184610ca5565b92915050565b610cd881610c23565b82525050565b6000602082019050610cf36000830184610ccf565b92915050565b600080600060608486031215610d1257610d11610bc0565b5b6000610d2086828701610c0e565b9350506020610d3186828701610c0e565b9250506040610d4286828701610c44565b9150509250925092565b600060ff82169050919050565b610d6281610d4c565b82525050565b6000602082019050610d7d6000830184610d59565b92915050565b600060208284031215610d9957610d98610bc0565b5b6000610da784828501610c0e565b91505092915050565b60008060408385031215610dc757610dc6610bc0565b5b6000610dd585828601610c0e565b9250506020610de685828601610c0e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610e3757607f821691505b602082108103610e4a57610e49610df0565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e8a82610c23565b9150610e9583610c23565b9250828201905080821115610ead57610eac610e50565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610f0f602583610b19565b9150610f1a82610eb3565b604082019050919050565b60006020820190508181036000830152610f3e81610f02565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000610fa1602483610b19565b9150610fac82610f45565b604082019050919050565b60006020820190508181036000830152610fd081610f94565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000611033602283610b19565b915061103e82610fd7565b604082019050919050565b6000602082019050818103600083015261106281611026565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061109f601d83610b19565b91506110aa82611069565b602082019050919050565b600060208201905081810360008301526110ce81611092565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000611131602583610b19565b915061113c826110d5565b604082019050919050565b6000602082019050818103600083015261116081611124565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b60006111c3602383610b19565b91506111ce82611167565b604082019050919050565b600060208201905081810360008301526111f2816111b6565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b6000611255602683610b19565b9150611260826111f9565b604082019050919050565b6000602082019050818103600083015261128481611248565b9050919050565b600061129682610c23565b91506112a183610c23565b92508282026112af81610c23565b915082820484148315176112c6576112c5610e50565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061130782610c23565b915061131283610c23565b925082611322576113216112cd565b5b828204905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fdfea2646970667358221220e34d9015349ff6af20fee44587dc5ba21b370ca2a6239a1dc6440ecaddfd47a364736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestERC20TransferThenPrecompileSend.sol/TestERC20TransferThenPrecompileSend.json b/x/evm/embeds/artifacts/contracts/TestERC20TransferThenPrecompileSend.sol/TestERC20TransferThenPrecompileSend.json new file mode 100644 index 000000000..59cb90cdb --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestERC20TransferThenPrecompileSend.sol/TestERC20TransferThenPrecompileSend.json @@ -0,0 +1,50 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestERC20TransferThenPrecompileSend", + "sourceName": "contracts/TestERC20TransferThenPrecompileSend.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "erc20_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "transferRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "transferAmount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "precompileRecipient", + "type": "string" + }, + { + "internalType": "uint256", + "name": "precompileAmount", + "type": "uint256" + } + ], + "name": "erc20TransferThenPrecompileSend", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50604051610c4c380380610c4c833981810160405281019061003291906100db565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610108565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b6100b88161009d565b81146100c357600080fd5b50565b6000815190506100d5816100af565b92915050565b6000602082840312156100f1576100f0610078565b5b60006100ff848285016100c6565b91505092915050565b610b35806101176000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063264c325814610030575b600080fd5b61004a6004803603810190610045919061065c565b61004c565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85856040518363ffffffff1660e01b81526004016100a792919061074d565b6020604051808303816000875af11580156100c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100ea91906107ae565b610129576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012090610838565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684866040518463ffffffff1660e01b815260040161018a939291906108e7565b6020604051808303816000875af11580156101a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101cd919061093a565b90508181146101db8261024d565b6101e48461024d565b6040516020016101f5929190610a61565b60405160208183030381529060405290610245576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161023c9190610aae565b60405180910390fd5b505050505050565b60606000600161025c8461031b565b01905060008167ffffffffffffffff81111561027b5761027a610531565b5b6040519080825280601f01601f1916602001820160405280156102ad5781602001600182028036833780820191505090505b509050600082602001820190505b600115610310578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a858161030457610303610ad0565b5b049450600085036102bb575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310610379577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161036f5761036e610ad0565b5b0492506040810190505b6d04ee2d6d415b85acef810000000083106103b6576d04ee2d6d415b85acef810000000083816103ac576103ab610ad0565b5b0492506020810190505b662386f26fc1000083106103e557662386f26fc1000083816103db576103da610ad0565b5b0492506010810190505b6305f5e100831061040e576305f5e100838161040457610403610ad0565b5b0492506008810190505b612710831061043357612710838161042957610428610ad0565b5b0492506004810190505b60648310610456576064838161044c5761044b610ad0565b5b0492506002810190505b600a8310610465576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104ad82610482565b9050919050565b6104bd816104a2565b81146104c857600080fd5b50565b6000813590506104da816104b4565b92915050565b6000819050919050565b6104f3816104e0565b81146104fe57600080fd5b50565b600081359050610510816104ea565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61056982610520565b810181811067ffffffffffffffff8211171561058857610587610531565b5b80604052505050565b600061059b61046e565b90506105a78282610560565b919050565b600067ffffffffffffffff8211156105c7576105c6610531565b5b6105d082610520565b9050602081019050919050565b82818337600083830152505050565b60006105ff6105fa846105ac565b610591565b90508281526020810184848401111561061b5761061a61051b565b5b6106268482856105dd565b509392505050565b600082601f83011261064357610642610516565b5b81356106538482602086016105ec565b91505092915050565b6000806000806080858703121561067657610675610478565b5b6000610684878288016104cb565b945050602061069587828801610501565b935050604085013567ffffffffffffffff8111156106b6576106b561047d565b5b6106c28782880161062e565b92505060606106d387828801610501565b91505092959194509250565b6000819050919050565b60006107046106ff6106fa84610482565b6106df565b610482565b9050919050565b6000610716826106e9565b9050919050565b60006107288261070b565b9050919050565b6107388161071d565b82525050565b610747816104e0565b82525050565b6000604082019050610762600083018561072f565b61076f602083018461073e565b9392505050565b60008115159050919050565b61078b81610776565b811461079657600080fd5b50565b6000815190506107a881610782565b92915050565b6000602082840312156107c4576107c3610478565b5b60006107d284828501610799565b91505092915050565b600082825260208201905092915050565b7f4552432d3230207472616e73666572206661696c656400000000000000000000600082015250565b60006108226016836107db565b915061082d826107ec565b602082019050919050565b6000602082019050818103600083015261085181610815565b9050919050565b600061086382610482565b9050919050565b61087381610858565b82525050565b600081519050919050565b60005b838110156108a2578082015181840152602081019050610887565b60008484015250505050565b60006108b982610879565b6108c381856107db565b93506108d3818560208601610884565b6108dc81610520565b840191505092915050565b60006060820190506108fc600083018661086a565b610909602083018561073e565b818103604083015261091b81846108ae565b9050949350505050565b600081519050610934816104ea565b92915050565b6000602082840312156109505761094f610478565b5b600061095e84828501610925565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e73656e64546f42616e6b20737563636565646564206260008201527f7574207472616e73666572726564207468652077726f6e6720616d6f756e7400602082015250565b60006109ce603f83610967565b91506109d982610972565b603f82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b6000610a1582610879565b610a1f8185610967565b9350610a2f818560208601610884565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b6000610a6c826109c1565b9150610a77826109e4565b600b82019150610a878285610a0a565b9150610a9282610a3b565b600982019150610aa28284610a0a565b91508190509392505050565b60006020820190508181036000830152610ac881846108ae565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea264697066735822122098bce4f3063b087b07297f4fc552a93abc1e02f3c0e662745934946c8edc9afb64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063264c325814610030575b600080fd5b61004a6004803603810190610045919061065c565b61004c565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85856040518363ffffffff1660e01b81526004016100a792919061074d565b6020604051808303816000875af11580156100c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100ea91906107ae565b610129576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012090610838565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684866040518463ffffffff1660e01b815260040161018a939291906108e7565b6020604051808303816000875af11580156101a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101cd919061093a565b90508181146101db8261024d565b6101e48461024d565b6040516020016101f5929190610a61565b60405160208183030381529060405290610245576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161023c9190610aae565b60405180910390fd5b505050505050565b60606000600161025c8461031b565b01905060008167ffffffffffffffff81111561027b5761027a610531565b5b6040519080825280601f01601f1916602001820160405280156102ad5781602001600182028036833780820191505090505b509050600082602001820190505b600115610310578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a858161030457610303610ad0565b5b049450600085036102bb575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310610379577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161036f5761036e610ad0565b5b0492506040810190505b6d04ee2d6d415b85acef810000000083106103b6576d04ee2d6d415b85acef810000000083816103ac576103ab610ad0565b5b0492506020810190505b662386f26fc1000083106103e557662386f26fc1000083816103db576103da610ad0565b5b0492506010810190505b6305f5e100831061040e576305f5e100838161040457610403610ad0565b5b0492506008810190505b612710831061043357612710838161042957610428610ad0565b5b0492506004810190505b60648310610456576064838161044c5761044b610ad0565b5b0492506002810190505b600a8310610465576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104ad82610482565b9050919050565b6104bd816104a2565b81146104c857600080fd5b50565b6000813590506104da816104b4565b92915050565b6000819050919050565b6104f3816104e0565b81146104fe57600080fd5b50565b600081359050610510816104ea565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61056982610520565b810181811067ffffffffffffffff8211171561058857610587610531565b5b80604052505050565b600061059b61046e565b90506105a78282610560565b919050565b600067ffffffffffffffff8211156105c7576105c6610531565b5b6105d082610520565b9050602081019050919050565b82818337600083830152505050565b60006105ff6105fa846105ac565b610591565b90508281526020810184848401111561061b5761061a61051b565b5b6106268482856105dd565b509392505050565b600082601f83011261064357610642610516565b5b81356106538482602086016105ec565b91505092915050565b6000806000806080858703121561067657610675610478565b5b6000610684878288016104cb565b945050602061069587828801610501565b935050604085013567ffffffffffffffff8111156106b6576106b561047d565b5b6106c28782880161062e565b92505060606106d387828801610501565b91505092959194509250565b6000819050919050565b60006107046106ff6106fa84610482565b6106df565b610482565b9050919050565b6000610716826106e9565b9050919050565b60006107288261070b565b9050919050565b6107388161071d565b82525050565b610747816104e0565b82525050565b6000604082019050610762600083018561072f565b61076f602083018461073e565b9392505050565b60008115159050919050565b61078b81610776565b811461079657600080fd5b50565b6000815190506107a881610782565b92915050565b6000602082840312156107c4576107c3610478565b5b60006107d284828501610799565b91505092915050565b600082825260208201905092915050565b7f4552432d3230207472616e73666572206661696c656400000000000000000000600082015250565b60006108226016836107db565b915061082d826107ec565b602082019050919050565b6000602082019050818103600083015261085181610815565b9050919050565b600061086382610482565b9050919050565b61087381610858565b82525050565b600081519050919050565b60005b838110156108a2578082015181840152602081019050610887565b60008484015250505050565b60006108b982610879565b6108c381856107db565b93506108d3818560208601610884565b6108dc81610520565b840191505092915050565b60006060820190506108fc600083018661086a565b610909602083018561073e565b818103604083015261091b81846108ae565b9050949350505050565b600081519050610934816104ea565b92915050565b6000602082840312156109505761094f610478565b5b600061095e84828501610925565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e73656e64546f42616e6b20737563636565646564206260008201527f7574207472616e73666572726564207468652077726f6e6720616d6f756e7400602082015250565b60006109ce603f83610967565b91506109d982610972565b603f82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b6000610a1582610879565b610a1f8185610967565b9350610a2f818560208601610884565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b6000610a6c826109c1565b9150610a77826109e4565b600b82019150610a878285610a0a565b9150610a9282610a3b565b600982019150610aa28284610a0a565b91508190509392505050565b60006020820190508181036000830152610ac881846108ae565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea264697066735822122098bce4f3063b087b07297f4fc552a93abc1e02f3c0e662745934946c8edc9afb64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestERC20TransferWithFee.sol/TestERC20TransferWithFee.json b/x/evm/embeds/artifacts/contracts/TestERC20TransferWithFee.sol/TestERC20TransferWithFee.json new file mode 100644 index 000000000..a3ad2ea1e --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestERC20TransferWithFee.sol/TestERC20TransferWithFee.json @@ -0,0 +1,297 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestERC20TransferWithFee", + "sourceName": "contracts/TestERC20TransferWithFee.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162001c7b38038062001c7b833981810160405281019062000037919062000385565b818181600390816200004a919062000655565b5080600490816200005c919062000655565b50505062000073336103e86200007b60201b60201c565b505062000857565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603620000ed576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000e4906200079d565b60405180910390fd5b6200010160008383620001e860201b60201c565b8060026000828254620001159190620007ee565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620001c891906200083a565b60405180910390a3620001e460008383620001ed60201b60201c565b5050565b505050565b505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200025b8262000210565b810181811067ffffffffffffffff821117156200027d576200027c62000221565b5b80604052505050565b600062000292620001f2565b9050620002a0828262000250565b919050565b600067ffffffffffffffff821115620002c357620002c262000221565b5b620002ce8262000210565b9050602081019050919050565b60005b83811015620002fb578082015181840152602081019050620002de565b60008484015250505050565b60006200031e6200031884620002a5565b62000286565b9050828152602081018484840111156200033d576200033c6200020b565b5b6200034a848285620002db565b509392505050565b600082601f8301126200036a576200036962000206565b5b81516200037c84826020860162000307565b91505092915050565b600080604083850312156200039f576200039e620001fc565b5b600083015167ffffffffffffffff811115620003c057620003bf62000201565b5b620003ce8582860162000352565b925050602083015167ffffffffffffffff811115620003f257620003f162000201565b5b620004008582860162000352565b9150509250929050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200045d57607f821691505b60208210810362000473576200047262000415565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620004dd7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826200049e565b620004e986836200049e565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b600062000536620005306200052a8462000501565b6200050b565b62000501565b9050919050565b6000819050919050565b620005528362000515565b6200056a62000561826200053d565b848454620004ab565b825550505050565b600090565b6200058162000572565b6200058e81848462000547565b505050565b5b81811015620005b657620005aa60008262000577565b60018101905062000594565b5050565b601f8211156200060557620005cf8162000479565b620005da846200048e565b81016020851015620005ea578190505b62000602620005f9856200048e565b83018262000593565b50505b505050565b600082821c905092915050565b60006200062a600019846008026200060a565b1980831691505092915050565b600062000645838362000617565b9150826002028217905092915050565b62000660826200040a565b67ffffffffffffffff8111156200067c576200067b62000221565b5b62000688825462000444565b62000695828285620005ba565b600060209050601f831160018114620006cd5760008415620006b8578287015190505b620006c4858262000637565b86555062000734565b601f198416620006dd8662000479565b60005b828110156200070757848901518255600182019150602085019450602081019050620006e0565b8683101562000727578489015162000723601f89168262000617565b8355505b6001600288020188555050505b505050505050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b600062000785601f836200073c565b915062000792826200074d565b602082019050919050565b60006020820190508181036000830152620007b88162000776565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000620007fb8262000501565b9150620008088362000501565b9250828201905080821115620008235762000822620007bf565b5b92915050565b620008348162000501565b82525050565b600060208201905062000851600083018462000829565b92915050565b61141480620008676000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b89565b60405180910390f35b6100e660048036038101906100e19190610c44565b610308565b6040516100f39190610c9f565b60405180910390f35b61010461032b565b6040516101119190610cc9565b60405180910390f35b610134600480360381019061012f9190610ce4565b610335565b6040516101419190610c9f565b60405180910390f35b610152610364565b60405161015f9190610d53565b60405180910390f35b610182600480360381019061017d9190610c44565b61036d565b60405161018f9190610c9f565b60405180910390f35b6101b260048036038101906101ad9190610d6e565b6103a4565b6040516101bf9190610cc9565b60405180910390f35b6101d06103ec565b6040516101dd9190610b89565b60405180910390f35b61020060048036038101906101fb9190610c44565b61047e565b60405161020d9190610c9f565b60405180910390f35b610230600480360381019061022b9190610c44565b6104f5565b60405161023d9190610c9f565b60405180910390f35b610260600480360381019061025b9190610d9b565b610595565b60405161026d9190610cc9565b60405180910390f35b60606003805461028590610e0a565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190610e0a565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b60008061031361061c565b9050610320818585610624565b600191505092915050565b6000600254905090565b60008061034061061c565b905061034d8582856107ed565b610358858585610879565b60019150509392505050565b60006012905090565b60008061037861061c565b905061039981858561038a8589610595565b6103949190610e6a565b610624565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060600480546103fb90610e0a565b80601f016020809104026020016040519081016040528092919081815260200182805461042790610e0a565b80156104745780601f1061044957610100808354040283529160200191610474565b820191906000526020600020905b81548152906001019060200180831161045757829003601f168201915b5050505050905090565b60008061048961061c565b905060006104978286610595565b9050838110156104dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104d390610f10565b60405180910390fd5b6104e98286868403610624565b60019250505092915050565b60008061050061061c565b905060008311610545576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161053c90610fa2565b60405180910390fd5b60006064600a856105569190610fc2565b6105609190611033565b9050600081856105709190611064565b905061057d833084610879565b610588838783610879565b6001935050505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610693576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161068a9061110a565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610702576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106f99061119c565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516107e09190610cc9565b60405180910390a3505050565b60006107f98484610595565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146108735781811015610865576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085c90611208565b60405180910390fd5b6108728484848403610624565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108df9061129a565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610957576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094e9061132c565b60405180910390fd5b610962838383610aef565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156109e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109df906113be565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610ad69190610cc9565b60405180910390a3610ae9848484610af4565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b33578082015181840152602081019050610b18565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b5b82610af9565b610b658185610b04565b9350610b75818560208601610b15565b610b7e81610b3f565b840191505092915050565b60006020820190508181036000830152610ba38184610b50565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bdb82610bb0565b9050919050565b610beb81610bd0565b8114610bf657600080fd5b50565b600081359050610c0881610be2565b92915050565b6000819050919050565b610c2181610c0e565b8114610c2c57600080fd5b50565b600081359050610c3e81610c18565b92915050565b60008060408385031215610c5b57610c5a610bab565b5b6000610c6985828601610bf9565b9250506020610c7a85828601610c2f565b9150509250929050565b60008115159050919050565b610c9981610c84565b82525050565b6000602082019050610cb46000830184610c90565b92915050565b610cc381610c0e565b82525050565b6000602082019050610cde6000830184610cba565b92915050565b600080600060608486031215610cfd57610cfc610bab565b5b6000610d0b86828701610bf9565b9350506020610d1c86828701610bf9565b9250506040610d2d86828701610c2f565b9150509250925092565b600060ff82169050919050565b610d4d81610d37565b82525050565b6000602082019050610d686000830184610d44565b92915050565b600060208284031215610d8457610d83610bab565b5b6000610d9284828501610bf9565b91505092915050565b60008060408385031215610db257610db1610bab565b5b6000610dc085828601610bf9565b9250506020610dd185828601610bf9565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610e2257607f821691505b602082108103610e3557610e34610ddb565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e7582610c0e565b9150610e8083610c0e565b9250828201905080821115610e9857610e97610e3b565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610efa602583610b04565b9150610f0582610e9e565b604082019050919050565b60006020820190508181036000830152610f2981610eed565b9050919050565b7f5472616e7366657220616d6f756e74206d75737420626520677265617465722060008201527f7468616e207a65726f0000000000000000000000000000000000000000000000602082015250565b6000610f8c602983610b04565b9150610f9782610f30565b604082019050919050565b60006020820190508181036000830152610fbb81610f7f565b9050919050565b6000610fcd82610c0e565b9150610fd883610c0e565b9250828202610fe681610c0e565b91508282048414831517610ffd57610ffc610e3b565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061103e82610c0e565b915061104983610c0e565b92508261105957611058611004565b5b828204905092915050565b600061106f82610c0e565b915061107a83610c0e565b925082820390508181111561109257611091610e3b565b5b92915050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b60006110f4602483610b04565b91506110ff82611098565b604082019050919050565b60006020820190508181036000830152611123816110e7565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000611186602283610b04565b91506111918261112a565b604082019050919050565b600060208201905081810360008301526111b581611179565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b60006111f2601d83610b04565b91506111fd826111bc565b602082019050919050565b60006020820190508181036000830152611221816111e5565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000611284602583610b04565b915061128f82611228565b604082019050919050565b600060208201905081810360008301526112b381611277565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611316602383610b04565b9150611321826112ba565b604082019050919050565b6000602082019050818103600083015261134581611309565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b60006113a8602683610b04565b91506113b38261134c565b604082019050919050565b600060208201905081810360008301526113d78161139b565b905091905056fea2646970667358221220a163955cd8b44c46d18ec3c2ccad0a81dbb6f9a839f8fde7ac6328ed63ead16d64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b89565b60405180910390f35b6100e660048036038101906100e19190610c44565b610308565b6040516100f39190610c9f565b60405180910390f35b61010461032b565b6040516101119190610cc9565b60405180910390f35b610134600480360381019061012f9190610ce4565b610335565b6040516101419190610c9f565b60405180910390f35b610152610364565b60405161015f9190610d53565b60405180910390f35b610182600480360381019061017d9190610c44565b61036d565b60405161018f9190610c9f565b60405180910390f35b6101b260048036038101906101ad9190610d6e565b6103a4565b6040516101bf9190610cc9565b60405180910390f35b6101d06103ec565b6040516101dd9190610b89565b60405180910390f35b61020060048036038101906101fb9190610c44565b61047e565b60405161020d9190610c9f565b60405180910390f35b610230600480360381019061022b9190610c44565b6104f5565b60405161023d9190610c9f565b60405180910390f35b610260600480360381019061025b9190610d9b565b610595565b60405161026d9190610cc9565b60405180910390f35b60606003805461028590610e0a565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190610e0a565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b60008061031361061c565b9050610320818585610624565b600191505092915050565b6000600254905090565b60008061034061061c565b905061034d8582856107ed565b610358858585610879565b60019150509392505050565b60006012905090565b60008061037861061c565b905061039981858561038a8589610595565b6103949190610e6a565b610624565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060600480546103fb90610e0a565b80601f016020809104026020016040519081016040528092919081815260200182805461042790610e0a565b80156104745780601f1061044957610100808354040283529160200191610474565b820191906000526020600020905b81548152906001019060200180831161045757829003601f168201915b5050505050905090565b60008061048961061c565b905060006104978286610595565b9050838110156104dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104d390610f10565b60405180910390fd5b6104e98286868403610624565b60019250505092915050565b60008061050061061c565b905060008311610545576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161053c90610fa2565b60405180910390fd5b60006064600a856105569190610fc2565b6105609190611033565b9050600081856105709190611064565b905061057d833084610879565b610588838783610879565b6001935050505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610693576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161068a9061110a565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610702576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106f99061119c565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516107e09190610cc9565b60405180910390a3505050565b60006107f98484610595565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146108735781811015610865576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161085c90611208565b60405180910390fd5b6108728484848403610624565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108df9061129a565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610957576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094e9061132c565b60405180910390fd5b610962838383610aef565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156109e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109df906113be565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610ad69190610cc9565b60405180910390a3610ae9848484610af4565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b33578082015181840152602081019050610b18565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b5b82610af9565b610b658185610b04565b9350610b75818560208601610b15565b610b7e81610b3f565b840191505092915050565b60006020820190508181036000830152610ba38184610b50565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bdb82610bb0565b9050919050565b610beb81610bd0565b8114610bf657600080fd5b50565b600081359050610c0881610be2565b92915050565b6000819050919050565b610c2181610c0e565b8114610c2c57600080fd5b50565b600081359050610c3e81610c18565b92915050565b60008060408385031215610c5b57610c5a610bab565b5b6000610c6985828601610bf9565b9250506020610c7a85828601610c2f565b9150509250929050565b60008115159050919050565b610c9981610c84565b82525050565b6000602082019050610cb46000830184610c90565b92915050565b610cc381610c0e565b82525050565b6000602082019050610cde6000830184610cba565b92915050565b600080600060608486031215610cfd57610cfc610bab565b5b6000610d0b86828701610bf9565b9350506020610d1c86828701610bf9565b9250506040610d2d86828701610c2f565b9150509250925092565b600060ff82169050919050565b610d4d81610d37565b82525050565b6000602082019050610d686000830184610d44565b92915050565b600060208284031215610d8457610d83610bab565b5b6000610d9284828501610bf9565b91505092915050565b60008060408385031215610db257610db1610bab565b5b6000610dc085828601610bf9565b9250506020610dd185828601610bf9565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610e2257607f821691505b602082108103610e3557610e34610ddb565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e7582610c0e565b9150610e8083610c0e565b9250828201905080821115610e9857610e97610e3b565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610efa602583610b04565b9150610f0582610e9e565b604082019050919050565b60006020820190508181036000830152610f2981610eed565b9050919050565b7f5472616e7366657220616d6f756e74206d75737420626520677265617465722060008201527f7468616e207a65726f0000000000000000000000000000000000000000000000602082015250565b6000610f8c602983610b04565b9150610f9782610f30565b604082019050919050565b60006020820190508181036000830152610fbb81610f7f565b9050919050565b6000610fcd82610c0e565b9150610fd883610c0e565b9250828202610fe681610c0e565b91508282048414831517610ffd57610ffc610e3b565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061103e82610c0e565b915061104983610c0e565b92508261105957611058611004565b5b828204905092915050565b600061106f82610c0e565b915061107a83610c0e565b925082820390508181111561109257611091610e3b565b5b92915050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b60006110f4602483610b04565b91506110ff82611098565b604082019050919050565b60006020820190508181036000830152611123816110e7565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000611186602283610b04565b91506111918261112a565b604082019050919050565b600060208201905081810360008301526111b581611179565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b60006111f2601d83610b04565b91506111fd826111bc565b602082019050919050565b60006020820190508181036000830152611221816111e5565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b6000611284602583610b04565b915061128f82611228565b604082019050919050565b600060208201905081810360008301526112b381611277565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611316602383610b04565b9150611321826112ba565b604082019050919050565b6000602082019050818103600083015261134581611309565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b60006113a8602683610b04565b91506113b38261134c565b604082019050919050565b600060208201905081810360008301526113d78161139b565b905091905056fea2646970667358221220a163955cd8b44c46d18ec3c2ccad0a81dbb6f9a839f8fde7ac6328ed63ead16d64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestFunTokenPrecompileLocalGas.sol/TestFunTokenPrecompileLocalGas.json b/x/evm/embeds/artifacts/contracts/TestFunTokenPrecompileLocalGas.sol/TestFunTokenPrecompileLocalGas.json new file mode 100644 index 000000000..2bc45fbe5 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestFunTokenPrecompileLocalGas.sol/TestFunTokenPrecompileLocalGas.json @@ -0,0 +1,63 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestFunTokenPrecompileLocalGas", + "sourceName": "contracts/TestFunTokenPrecompileLocalGas.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "erc20_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "bech32Recipient", + "type": "string" + } + ], + "name": "callBankSend", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "bech32Recipient", + "type": "string" + }, + { + "internalType": "uint256", + "name": "customGas", + "type": "uint256" + } + ], + "name": "callBankSendLocalGas", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50604051610b6a380380610b6a833981810160405281019061003291906100db565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610108565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b6100b88161009d565b81146100c357600080fd5b50565b6000815190506100d5816100af565b92915050565b6000602082840312156100f1576100f0610078565b5b60006100ff848285016100c6565b91505092915050565b610a53806101176000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806359b6ed891461003b57806390d2b5e714610057575b600080fd5b6100556004803603810190610050919061066b565b610073565b005b610071600480360381019061006c91906106da565b610198565b005b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf8360008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1687876040518563ffffffff1660e01b81526004016100d593929190610805565b60206040518083038160008887f11580156100f4573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906101199190610858565b9050838114610127826102ba565b610130866102ba565b60405160200161014192919061097f565b60405160208183030381529060405290610191576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018891906109cc565b60405180910390fd5b5050505050565b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685856040518463ffffffff1660e01b81526004016101f993929190610805565b6020604051808303816000875af1158015610218573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023c9190610858565b905082811461024a826102ba565b610253856102ba565b60405160200161026492919061097f565b604051602081830303815290604052906102b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102ab91906109cc565b60405180910390fd5b50505050565b6060600060016102c984610388565b01905060008167ffffffffffffffff8111156102e8576102e7610540565b5b6040519080825280601f01601f19166020018201604052801561031a5781602001600182028036833780820191505090505b509050600082602001820190505b60011561037d578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8581610371576103706109ee565b5b04945060008503610328575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083106103e6577a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083816103dc576103db6109ee565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310610423576d04ee2d6d415b85acef81000000008381610419576104186109ee565b5b0492506020810190505b662386f26fc10000831061045257662386f26fc100008381610448576104476109ee565b5b0492506010810190505b6305f5e100831061047b576305f5e1008381610471576104706109ee565b5b0492506008810190505b61271083106104a0576127108381610496576104956109ee565b5b0492506004810190505b606483106104c357606483816104b9576104b86109ee565b5b0492506002810190505b600a83106104d2576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b610502816104ef565b811461050d57600080fd5b50565b60008135905061051f816104f9565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6105788261052f565b810181811067ffffffffffffffff8211171561059757610596610540565b5b80604052505050565b60006105aa6104db565b90506105b6828261056f565b919050565b600067ffffffffffffffff8211156105d6576105d5610540565b5b6105df8261052f565b9050602081019050919050565b82818337600083830152505050565b600061060e610609846105bb565b6105a0565b90508281526020810184848401111561062a5761062961052a565b5b6106358482856105ec565b509392505050565b600082601f83011261065257610651610525565b5b81356106628482602086016105fb565b91505092915050565b600080600060608486031215610684576106836104e5565b5b600061069286828701610510565b935050602084013567ffffffffffffffff8111156106b3576106b26104ea565b5b6106bf8682870161063d565b92505060406106d086828701610510565b9150509250925092565b600080604083850312156106f1576106f06104e5565b5b60006106ff85828601610510565b925050602083013567ffffffffffffffff8111156107205761071f6104ea565b5b61072c8582860161063d565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061076182610736565b9050919050565b61077181610756565b82525050565b610780816104ef565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b838110156107c05780820151818401526020810190506107a5565b60008484015250505050565b60006107d782610786565b6107e18185610791565b93506107f18185602086016107a2565b6107fa8161052f565b840191505092915050565b600060608201905061081a6000830186610768565b6108276020830185610777565b818103604083015261083981846107cc565b9050949350505050565b600081519050610852816104f9565b92915050565b60006020828403121561086e5761086d6104e5565b5b600061087c84828501610843565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e73656e64546f42616e6b20737563636565646564206260008201527f7574207472616e73666572726564207468652077726f6e6720616d6f756e7400602082015250565b60006108ec603f83610885565b91506108f782610890565b603f82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b600061093382610786565b61093d8185610885565b935061094d8185602086016107a2565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b600061098a826108df565b915061099582610902565b600b820191506109a58285610928565b91506109b082610959565b6009820191506109c08284610928565b91508190509392505050565b600060208201905081810360008301526109e681846107cc565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea26469706673582212205f97d0338f2c6e64505fe5f37c42a0d652e38e5cf3e47895d1a2ceedd05ef4ae64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806359b6ed891461003b57806390d2b5e714610057575b600080fd5b6100556004803603810190610050919061066b565b610073565b005b610071600480360381019061006c91906106da565b610198565b005b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf8360008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1687876040518563ffffffff1660e01b81526004016100d593929190610805565b60206040518083038160008887f11580156100f4573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906101199190610858565b9050838114610127826102ba565b610130866102ba565b60405160200161014192919061097f565b60405160208183030381529060405290610191576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018891906109cc565b60405180910390fd5b5050505050565b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685856040518463ffffffff1660e01b81526004016101f993929190610805565b6020604051808303816000875af1158015610218573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023c9190610858565b905082811461024a826102ba565b610253856102ba565b60405160200161026492919061097f565b604051602081830303815290604052906102b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102ab91906109cc565b60405180910390fd5b50505050565b6060600060016102c984610388565b01905060008167ffffffffffffffff8111156102e8576102e7610540565b5b6040519080825280601f01601f19166020018201604052801561031a5781602001600182028036833780820191505090505b509050600082602001820190505b60011561037d578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a8581610371576103706109ee565b5b04945060008503610328575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083106103e6577a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083816103dc576103db6109ee565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310610423576d04ee2d6d415b85acef81000000008381610419576104186109ee565b5b0492506020810190505b662386f26fc10000831061045257662386f26fc100008381610448576104476109ee565b5b0492506010810190505b6305f5e100831061047b576305f5e1008381610471576104706109ee565b5b0492506008810190505b61271083106104a0576127108381610496576104956109ee565b5b0492506004810190505b606483106104c357606483816104b9576104b86109ee565b5b0492506002810190505b600a83106104d2576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b610502816104ef565b811461050d57600080fd5b50565b60008135905061051f816104f9565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6105788261052f565b810181811067ffffffffffffffff8211171561059757610596610540565b5b80604052505050565b60006105aa6104db565b90506105b6828261056f565b919050565b600067ffffffffffffffff8211156105d6576105d5610540565b5b6105df8261052f565b9050602081019050919050565b82818337600083830152505050565b600061060e610609846105bb565b6105a0565b90508281526020810184848401111561062a5761062961052a565b5b6106358482856105ec565b509392505050565b600082601f83011261065257610651610525565b5b81356106628482602086016105fb565b91505092915050565b600080600060608486031215610684576106836104e5565b5b600061069286828701610510565b935050602084013567ffffffffffffffff8111156106b3576106b26104ea565b5b6106bf8682870161063d565b92505060406106d086828701610510565b9150509250925092565b600080604083850312156106f1576106f06104e5565b5b60006106ff85828601610510565b925050602083013567ffffffffffffffff8111156107205761071f6104ea565b5b61072c8582860161063d565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061076182610736565b9050919050565b61077181610756565b82525050565b610780816104ef565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b838110156107c05780820151818401526020810190506107a5565b60008484015250505050565b60006107d782610786565b6107e18185610791565b93506107f18185602086016107a2565b6107fa8161052f565b840191505092915050565b600060608201905061081a6000830186610768565b6108276020830185610777565b818103604083015261083981846107cc565b9050949350505050565b600081519050610852816104f9565b92915050565b60006020828403121561086e5761086d6104e5565b5b600061087c84828501610843565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e73656e64546f42616e6b20737563636565646564206260008201527f7574207472616e73666572726564207468652077726f6e6720616d6f756e7400602082015250565b60006108ec603f83610885565b91506108f782610890565b603f82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b600061093382610786565b61093d8185610885565b935061094d8185602086016107a2565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b600061098a826108df565b915061099582610902565b600b820191506109a58285610928565b91506109b082610959565b6009820191506109c08284610928565b91508190509392505050565b600060208201905081810360008301526109e681846107cc565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea26469706673582212205f97d0338f2c6e64505fe5f37c42a0d652e38e5cf3e47895d1a2ceedd05ef4ae64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestInfiniteRecursionERC20.sol/TestInfiniteRecursionERC20.json b/x/evm/embeds/artifacts/contracts/TestInfiniteRecursionERC20.sol/TestInfiniteRecursionERC20.json new file mode 100644 index 000000000..94826ad44 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestInfiniteRecursionERC20.sol/TestInfiniteRecursionERC20.json @@ -0,0 +1,316 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestInfiniteRecursionERC20", + "sourceName": "contracts/TestInfiniteRecursionERC20.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "decimals_", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "attackBalance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "attackTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "who", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "addedValue", + "type": "uint256" + } + ], + "name": "increaseAllowance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162001df438038062001df48339818101604052810190620000379190620003cc565b828281600390816200004a9190620006b1565b5080600490816200005c9190620006b1565b5050506200007b3369d3c21bcecceda10000006200008460201b60201c565b505050620008b3565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603620000f6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000ed90620007f9565b60405180910390fd5b6200010a60008383620001f160201b60201c565b80600260008282546200011e91906200084a565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620001d1919062000896565b60405180910390a3620001ed60008383620001f660201b60201c565b5050565b505050565b505050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620002648262000219565b810181811067ffffffffffffffff821117156200028657620002856200022a565b5b80604052505050565b60006200029b620001fb565b9050620002a9828262000259565b919050565b600067ffffffffffffffff821115620002cc57620002cb6200022a565b5b620002d78262000219565b9050602081019050919050565b60005b8381101562000304578082015181840152602081019050620002e7565b60008484015250505050565b6000620003276200032184620002ae565b6200028f565b90508281526020810184848401111562000346576200034562000214565b5b62000353848285620002e4565b509392505050565b600082601f8301126200037357620003726200020f565b5b81516200038584826020860162000310565b91505092915050565b600060ff82169050919050565b620003a6816200038e565b8114620003b257600080fd5b50565b600081519050620003c6816200039b565b92915050565b600080600060608486031215620003e857620003e762000205565b5b600084015167ffffffffffffffff8111156200040957620004086200020a565b5b62000417868287016200035b565b935050602084015167ffffffffffffffff8111156200043b576200043a6200020a565b5b62000449868287016200035b565b92505060406200045c86828701620003b5565b9150509250925092565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620004b957607f821691505b602082108103620004cf57620004ce62000471565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620005397fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620004fa565b620005458683620004fa565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620005926200058c62000586846200055d565b62000567565b6200055d565b9050919050565b6000819050919050565b620005ae8362000571565b620005c6620005bd8262000599565b84845462000507565b825550505050565b600090565b620005dd620005ce565b620005ea818484620005a3565b505050565b5b81811015620006125762000606600082620005d3565b600181019050620005f0565b5050565b601f82111562000661576200062b81620004d5565b6200063684620004ea565b8101602085101562000646578190505b6200065e6200065585620004ea565b830182620005ef565b50505b505050565b600082821c905092915050565b6000620006866000198460080262000666565b1980831691505092915050565b6000620006a1838362000673565b9150826002028217905092915050565b620006bc8262000466565b67ffffffffffffffff811115620006d857620006d76200022a565b5b620006e48254620004a0565b620006f182828562000616565b600060209050601f83116001811462000729576000841562000714578287015190505b62000720858262000693565b86555062000790565b601f1984166200073986620004d5565b60005b8281101562000763578489015182556001820191506020850194506020810190506200073c565b868310156200078357848901516200077f601f89168262000673565b8355505b6001600288020188555050505b505050505050565b600082825260208201905092915050565b7f45524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b6000620007e1601f8362000798565b9150620007ee82620007a9565b602082019050919050565b600060208201905081810360008301526200081481620007d2565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600062000857826200055d565b915062000864836200055d565b92508282019050808211156200087f576200087e6200081b565b5b92915050565b62000890816200055d565b82525050565b6000602082019050620008ad600083018462000885565b92915050565b61153180620008c36000396000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b4114610202578063a457c2d714610220578063a9059cbb14610250578063dd62ed3e14610280576100cf565b806370a08231146101be5780637a7ffab0146101ee5780638cd64727146101f8576100cf565b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461012257806323b872dd14610140578063313ce56714610170578063395093511461018e575b600080fd5b6100dc6102b0565b6040516100e99190610c88565b60405180910390f35b61010c60048036038101906101079190610d43565b610342565b6040516101199190610d9e565b60405180910390f35b61012a610365565b6040516101379190610dc8565b60405180910390f35b61015a60048036038101906101559190610de3565b61036f565b6040516101679190610d9e565b60405180910390f35b61017861039e565b6040516101859190610e52565b60405180910390f35b6101a860048036038101906101a39190610d43565b6103a7565b6040516101b59190610d9e565b60405180910390f35b6101d860048036038101906101d39190610e6d565b6103de565b6040516101e59190610dc8565b60405180910390f35b6101f66104e2565b005b6102006104f1565b005b61020a6104fe565b6040516102179190610c88565b60405180910390f35b61023a60048036038101906102359190610d43565b610590565b6040516102479190610d9e565b60405180910390f35b61026a60048036038101906102659190610d43565b610607565b6040516102779190610d9e565b60405180910390f35b61029a60048036038101906102959190610e9a565b610694565b6040516102a79190610dc8565b60405180910390f35b6060600380546102bf90610f09565b80601f01602080910402602001604051908101604052809291908181526020018280546102eb90610f09565b80156103385780601f1061030d57610100808354040283529160200191610338565b820191906000526020600020905b81548152906001019060200180831161031b57829003601f168201915b5050505050905090565b60008061034d61071b565b905061035a818585610723565b600191505092915050565b6000600254905090565b60008061037a61071b565b90506103878582856108ec565b610392858585610978565b60019150509392505050565b60006012905090565b6000806103b261071b565b90506103d38185856103c48589610694565b6103ce9190610f69565b610723565b600191505092915050565b600061080073ffffffffffffffffffffffffffffffffffffffff16823060405160240161040c929190610fac565b6040516020818303038152906040527fb203bb99000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610496919061101c565b600060405180830381855afa9150503d80600081146104d1576040519150601f19603f3d011682016040523d82523d6000602084013e6104d6565b606091505b50505060009050919050565b6104ee60006001610607565b50565b6104fb60006103de565b50565b60606004805461050d90610f09565b80601f016020809104026020016040519081016040528092919081815260200182805461053990610f09565b80156105865780601f1061055b57610100808354040283529160200191610586565b820191906000526020600020905b81548152906001019060200180831161056957829003601f168201915b5050505050905090565b60008061059b61071b565b905060006105a98286610694565b9050838110156105ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e5906110a5565b60405180910390fd5b6105fb8286868403610723565b60019250505092915050565b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf30846040518363ffffffff1660e01b8152600401610646929190611137565b6020604051808303816000875af1158015610665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106899190611188565b506001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610792576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078990611227565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610801576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107f8906112b9565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516108df9190610dc8565b60405180910390a3505050565b60006108f88484610694565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109725781811015610964576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161095b90611325565b60405180910390fd5b6109718484848403610723565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036109e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109de906113b7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a56576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a4d90611449565b60405180910390fd5b610a61838383610bee565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610ae7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ade906114db565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610bd59190610dc8565b60405180910390a3610be8848484610bf3565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610c32578082015181840152602081019050610c17565b60008484015250505050565b6000601f19601f8301169050919050565b6000610c5a82610bf8565b610c648185610c03565b9350610c74818560208601610c14565b610c7d81610c3e565b840191505092915050565b60006020820190508181036000830152610ca28184610c4f565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cda82610caf565b9050919050565b610cea81610ccf565b8114610cf557600080fd5b50565b600081359050610d0781610ce1565b92915050565b6000819050919050565b610d2081610d0d565b8114610d2b57600080fd5b50565b600081359050610d3d81610d17565b92915050565b60008060408385031215610d5a57610d59610caa565b5b6000610d6885828601610cf8565b9250506020610d7985828601610d2e565b9150509250929050565b60008115159050919050565b610d9881610d83565b82525050565b6000602082019050610db36000830184610d8f565b92915050565b610dc281610d0d565b82525050565b6000602082019050610ddd6000830184610db9565b92915050565b600080600060608486031215610dfc57610dfb610caa565b5b6000610e0a86828701610cf8565b9350506020610e1b86828701610cf8565b9250506040610e2c86828701610d2e565b9150509250925092565b600060ff82169050919050565b610e4c81610e36565b82525050565b6000602082019050610e676000830184610e43565b92915050565b600060208284031215610e8357610e82610caa565b5b6000610e9184828501610cf8565b91505092915050565b60008060408385031215610eb157610eb0610caa565b5b6000610ebf85828601610cf8565b9250506020610ed085828601610cf8565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610f2157607f821691505b602082108103610f3457610f33610eda565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610f7482610d0d565b9150610f7f83610d0d565b9250828201905080821115610f9757610f96610f3a565b5b92915050565b610fa681610ccf565b82525050565b6000604082019050610fc16000830185610f9d565b610fce6020830184610f9d565b9392505050565b600081519050919050565b600081905092915050565b6000610ff682610fd5565b6110008185610fe0565b9350611010818560208601610c14565b80840191505092915050565b60006110288284610feb565b915081905092915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b600061108f602583610c03565b915061109a82611033565b604082019050919050565b600060208201905081810360008301526110be81611082565b9050919050565b7f6e696269317a616176767a78657a30656c756e64746e3332716e6b396c6b6d3860008201527f6b6d63737a34346737786c000000000000000000000000000000000000000000602082015250565b6000611121602b83610c03565b915061112c826110c5565b604082019050919050565b600060608201905061114c6000830185610f9d565b6111596020830184610db9565b818103604083015261116a81611114565b90509392505050565b60008151905061118281610d17565b92915050565b60006020828403121561119e5761119d610caa565b5b60006111ac84828501611173565b91505092915050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000611211602483610c03565b915061121c826111b5565b604082019050919050565b6000602082019050818103600083015261124081611204565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b60006112a3602283610c03565b91506112ae82611247565b604082019050919050565b600060208201905081810360008301526112d281611296565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061130f601d83610c03565b915061131a826112d9565b602082019050919050565b6000602082019050818103600083015261133e81611302565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b60006113a1602583610c03565b91506113ac82611345565b604082019050919050565b600060208201905081810360008301526113d081611394565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611433602383610c03565b915061143e826113d7565b604082019050919050565b6000602082019050818103600083015261146281611426565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b60006114c5602683610c03565b91506114d082611469565b604082019050919050565b600060208201905081810360008301526114f4816114b8565b905091905056fea2646970667358221220dd4756f5c94c6238843743f162220b2e144b7f5bb1e805ee436ebcd1b47c24f864736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b4114610202578063a457c2d714610220578063a9059cbb14610250578063dd62ed3e14610280576100cf565b806370a08231146101be5780637a7ffab0146101ee5780638cd64727146101f8576100cf565b806306fdde03146100d4578063095ea7b3146100f257806318160ddd1461012257806323b872dd14610140578063313ce56714610170578063395093511461018e575b600080fd5b6100dc6102b0565b6040516100e99190610c88565b60405180910390f35b61010c60048036038101906101079190610d43565b610342565b6040516101199190610d9e565b60405180910390f35b61012a610365565b6040516101379190610dc8565b60405180910390f35b61015a60048036038101906101559190610de3565b61036f565b6040516101679190610d9e565b60405180910390f35b61017861039e565b6040516101859190610e52565b60405180910390f35b6101a860048036038101906101a39190610d43565b6103a7565b6040516101b59190610d9e565b60405180910390f35b6101d860048036038101906101d39190610e6d565b6103de565b6040516101e59190610dc8565b60405180910390f35b6101f66104e2565b005b6102006104f1565b005b61020a6104fe565b6040516102179190610c88565b60405180910390f35b61023a60048036038101906102359190610d43565b610590565b6040516102479190610d9e565b60405180910390f35b61026a60048036038101906102659190610d43565b610607565b6040516102779190610d9e565b60405180910390f35b61029a60048036038101906102959190610e9a565b610694565b6040516102a79190610dc8565b60405180910390f35b6060600380546102bf90610f09565b80601f01602080910402602001604051908101604052809291908181526020018280546102eb90610f09565b80156103385780601f1061030d57610100808354040283529160200191610338565b820191906000526020600020905b81548152906001019060200180831161031b57829003601f168201915b5050505050905090565b60008061034d61071b565b905061035a818585610723565b600191505092915050565b6000600254905090565b60008061037a61071b565b90506103878582856108ec565b610392858585610978565b60019150509392505050565b60006012905090565b6000806103b261071b565b90506103d38185856103c48589610694565b6103ce9190610f69565b610723565b600191505092915050565b600061080073ffffffffffffffffffffffffffffffffffffffff16823060405160240161040c929190610fac565b6040516020818303038152906040527fb203bb99000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051610496919061101c565b600060405180830381855afa9150503d80600081146104d1576040519150601f19603f3d011682016040523d82523d6000602084013e6104d6565b606091505b50505060009050919050565b6104ee60006001610607565b50565b6104fb60006103de565b50565b60606004805461050d90610f09565b80601f016020809104026020016040519081016040528092919081815260200182805461053990610f09565b80156105865780601f1061055b57610100808354040283529160200191610586565b820191906000526020600020905b81548152906001019060200180831161056957829003601f168201915b5050505050905090565b60008061059b61071b565b905060006105a98286610694565b9050838110156105ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e5906110a5565b60405180910390fd5b6105fb8286868403610723565b60019250505092915050565b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf30846040518363ffffffff1660e01b8152600401610646929190611137565b6020604051808303816000875af1158015610665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106899190611188565b506001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610792576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078990611227565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610801576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107f8906112b9565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516108df9190610dc8565b60405180910390a3505050565b60006108f88484610694565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146109725781811015610964576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161095b90611325565b60405180910390fd5b6109718484848403610723565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036109e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109de906113b7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a56576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a4d90611449565b60405180910390fd5b610a61838383610bee565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610ae7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ade906114db565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610bd59190610dc8565b60405180910390a3610be8848484610bf3565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610c32578082015181840152602081019050610c17565b60008484015250505050565b6000601f19601f8301169050919050565b6000610c5a82610bf8565b610c648185610c03565b9350610c74818560208601610c14565b610c7d81610c3e565b840191505092915050565b60006020820190508181036000830152610ca28184610c4f565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610cda82610caf565b9050919050565b610cea81610ccf565b8114610cf557600080fd5b50565b600081359050610d0781610ce1565b92915050565b6000819050919050565b610d2081610d0d565b8114610d2b57600080fd5b50565b600081359050610d3d81610d17565b92915050565b60008060408385031215610d5a57610d59610caa565b5b6000610d6885828601610cf8565b9250506020610d7985828601610d2e565b9150509250929050565b60008115159050919050565b610d9881610d83565b82525050565b6000602082019050610db36000830184610d8f565b92915050565b610dc281610d0d565b82525050565b6000602082019050610ddd6000830184610db9565b92915050565b600080600060608486031215610dfc57610dfb610caa565b5b6000610e0a86828701610cf8565b9350506020610e1b86828701610cf8565b9250506040610e2c86828701610d2e565b9150509250925092565b600060ff82169050919050565b610e4c81610e36565b82525050565b6000602082019050610e676000830184610e43565b92915050565b600060208284031215610e8357610e82610caa565b5b6000610e9184828501610cf8565b91505092915050565b60008060408385031215610eb157610eb0610caa565b5b6000610ebf85828601610cf8565b9250506020610ed085828601610cf8565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610f2157607f821691505b602082108103610f3457610f33610eda565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610f7482610d0d565b9150610f7f83610d0d565b9250828201905080821115610f9757610f96610f3a565b5b92915050565b610fa681610ccf565b82525050565b6000604082019050610fc16000830185610f9d565b610fce6020830184610f9d565b9392505050565b600081519050919050565b600081905092915050565b6000610ff682610fd5565b6110008185610fe0565b9350611010818560208601610c14565b80840191505092915050565b60006110288284610feb565b915081905092915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b600061108f602583610c03565b915061109a82611033565b604082019050919050565b600060208201905081810360008301526110be81611082565b9050919050565b7f6e696269317a616176767a78657a30656c756e64746e3332716e6b396c6b6d3860008201527f6b6d63737a34346737786c000000000000000000000000000000000000000000602082015250565b6000611121602b83610c03565b915061112c826110c5565b604082019050919050565b600060608201905061114c6000830185610f9d565b6111596020830184610db9565b818103604083015261116a81611114565b90509392505050565b60008151905061118281610d17565b92915050565b60006020828403121561119e5761119d610caa565b5b60006111ac84828501611173565b91505092915050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000611211602483610c03565b915061121c826111b5565b604082019050919050565b6000602082019050818103600083015261124081611204565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b60006112a3602283610c03565b91506112ae82611247565b604082019050919050565b600060208201905081810360008301526112d281611296565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061130f601d83610c03565b915061131a826112d9565b602082019050919050565b6000602082019050818103600083015261133e81611302565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b60006113a1602583610c03565b91506113ac82611345565b604082019050919050565b600060208201905081810360008301526113d081611394565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f6573730000000000000000000000000000000000000000000000000000000000602082015250565b6000611433602383610c03565b915061143e826113d7565b604082019050919050565b6000602082019050818103600083015261146281611426565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b60006114c5602683610c03565b91506114d082611469565b604082019050919050565b600060208201905081810360008301526114f4816114b8565b905091905056fea2646970667358221220dd4756f5c94c6238843743f162220b2e144b7f5bb1e805ee436ebcd1b47c24f864736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestNativeSendThenPrecompileSend.sol/TestNativeSendThenPrecompileSend.json b/x/evm/embeds/artifacts/contracts/TestNativeSendThenPrecompileSend.sol/TestNativeSendThenPrecompileSend.json new file mode 100644 index 000000000..643a9779b --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestNativeSendThenPrecompileSend.sol/TestNativeSendThenPrecompileSend.json @@ -0,0 +1,68 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestNativeSendThenPrecompileSend", + "sourceName": "contracts/TestNativeSendThenPrecompileSend.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "erc20_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "precompileRecipient", + "type": "string" + }, + { + "internalType": "uint256", + "name": "precompileAmount", + "type": "uint256" + } + ], + "name": "justPrecompileSend", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "nativeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nativeAmount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "precompileRecipient", + "type": "string" + }, + { + "internalType": "uint256", + "name": "precompileAmount", + "type": "uint256" + } + ], + "name": "nativeSendThenPrecompileSend", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50604051610ca1380380610ca1833981810160405281019061003291906100db565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610108565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b6100b88161009d565b81146100c357600080fd5b50565b6000815190506100d5816100af565b92915050565b6000602082840312156100f1576100f0610078565b5b60006100ff848285016100c6565b91505092915050565b610b8a806101176000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80630a04fd4d1461003b578063a4de557414610057575b600080fd5b610055600480360381019061005091906106e4565b610073565b005b610071600480360381019061006c919061079e565b610195565b005b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684866040518463ffffffff1660e01b81526004016100d4939291906108d0565b6020604051808303816000875af11580156100f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101179190610923565b905081811461012582610333565b61012e84610333565b60405160200161013f929190610a4a565b6040516020818303038152906040529061018f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101869190610a97565b60405180910390fd5b50505050565b60008473ffffffffffffffffffffffffffffffffffffffff166108fc859081150290604051600060405180830381858888f1935050505090508061020e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020590610b05565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685876040518463ffffffff1660e01b815260040161026f939291906108d0565b6020604051808303816000875af115801561028e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b29190610923565b90508281146102c082610333565b6102c985610333565b6040516020016102da929190610a4a565b6040516020818303038152906040529061032a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103219190610a97565b60405180910390fd5b50505050505050565b60606000600161034284610401565b01905060008167ffffffffffffffff81111561036157610360610583565b5b6040519080825280601f01601f1916602001820160405280156103935781602001600182028036833780820191505090505b509050600082602001820190505b6001156103f6578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a85816103ea576103e9610b25565b5b049450600085036103a1575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061045f577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161045557610454610b25565b5b0492506040810190505b6d04ee2d6d415b85acef8100000000831061049c576d04ee2d6d415b85acef8100000000838161049257610491610b25565b5b0492506020810190505b662386f26fc1000083106104cb57662386f26fc1000083816104c1576104c0610b25565b5b0492506010810190505b6305f5e10083106104f4576305f5e10083816104ea576104e9610b25565b5b0492506008810190505b612710831061051957612710838161050f5761050e610b25565b5b0492506004810190505b6064831061053c576064838161053257610531610b25565b5b0492506002810190505b600a831061054b576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6105bb82610572565b810181811067ffffffffffffffff821117156105da576105d9610583565b5b80604052505050565b60006105ed610554565b90506105f982826105b2565b919050565b600067ffffffffffffffff82111561061957610618610583565b5b61062282610572565b9050602081019050919050565b82818337600083830152505050565b600061065161064c846105fe565b6105e3565b90508281526020810184848401111561066d5761066c61056d565b5b61067884828561062f565b509392505050565b600082601f83011261069557610694610568565b5b81356106a584826020860161063e565b91505092915050565b6000819050919050565b6106c1816106ae565b81146106cc57600080fd5b50565b6000813590506106de816106b8565b92915050565b600080604083850312156106fb576106fa61055e565b5b600083013567ffffffffffffffff81111561071957610718610563565b5b61072585828601610680565b9250506020610736858286016106cf565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061076b82610740565b9050919050565b61077b81610760565b811461078657600080fd5b50565b60008135905061079881610772565b92915050565b600080600080608085870312156107b8576107b761055e565b5b60006107c687828801610789565b94505060206107d7878288016106cf565b935050604085013567ffffffffffffffff8111156107f8576107f7610563565b5b61080487828801610680565b9250506060610815878288016106cf565b91505092959194509250565b600061082c82610740565b9050919050565b61083c81610821565b82525050565b61084b816106ae565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561088b578082015181840152602081019050610870565b60008484015250505050565b60006108a282610851565b6108ac818561085c565b93506108bc81856020860161086d565b6108c581610572565b840191505092915050565b60006060820190506108e56000830186610833565b6108f26020830185610842565b81810360408301526109048184610897565b9050949350505050565b60008151905061091d816106b8565b92915050565b6000602082840312156109395761093861055e565b5b60006109478482850161090e565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e73656e64546f42616e6b20737563636565646564206260008201527f7574207472616e73666572726564207468652077726f6e6720616d6f756e7400602082015250565b60006109b7603f83610950565b91506109c28261095b565b603f82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b60006109fe82610851565b610a088185610950565b9350610a1881856020860161086d565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b6000610a55826109aa565b9150610a60826109cd565b600b82019150610a7082856109f3565b9150610a7b82610a24565b600982019150610a8b82846109f3565b91508190509392505050565b60006020820190508181036000830152610ab18184610897565b905092915050565b7f4661696c656420746f2073656e64206e617469766520746f6b656e0000000000600082015250565b6000610aef601b8361085c565b9150610afa82610ab9565b602082019050919050565b60006020820190508181036000830152610b1e81610ae2565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220ac863296eb3e4f13263954a8f80a733ab9593af91607e47e603b2f04399c60c964736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80630a04fd4d1461003b578063a4de557414610057575b600080fd5b610055600480360381019061005091906106e4565b610073565b005b610071600480360381019061006c919061079e565b610195565b005b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684866040518463ffffffff1660e01b81526004016100d4939291906108d0565b6020604051808303816000875af11580156100f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101179190610923565b905081811461012582610333565b61012e84610333565b60405160200161013f929190610a4a565b6040516020818303038152906040529061018f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101869190610a97565b60405180910390fd5b50505050565b60008473ffffffffffffffffffffffffffffffffffffffff166108fc859081150290604051600060405180830381858888f1935050505090508061020e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020590610b05565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685876040518463ffffffff1660e01b815260040161026f939291906108d0565b6020604051808303816000875af115801561028e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b29190610923565b90508281146102c082610333565b6102c985610333565b6040516020016102da929190610a4a565b6040516020818303038152906040529061032a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103219190610a97565b60405180910390fd5b50505050505050565b60606000600161034284610401565b01905060008167ffffffffffffffff81111561036157610360610583565b5b6040519080825280601f01601f1916602001820160405280156103935781602001600182028036833780820191505090505b509050600082602001820190505b6001156103f6578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a85816103ea576103e9610b25565b5b049450600085036103a1575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000831061045f577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161045557610454610b25565b5b0492506040810190505b6d04ee2d6d415b85acef8100000000831061049c576d04ee2d6d415b85acef8100000000838161049257610491610b25565b5b0492506020810190505b662386f26fc1000083106104cb57662386f26fc1000083816104c1576104c0610b25565b5b0492506010810190505b6305f5e10083106104f4576305f5e10083816104ea576104e9610b25565b5b0492506008810190505b612710831061051957612710838161050f5761050e610b25565b5b0492506004810190505b6064831061053c576064838161053257610531610b25565b5b0492506002810190505b600a831061054b576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6105bb82610572565b810181811067ffffffffffffffff821117156105da576105d9610583565b5b80604052505050565b60006105ed610554565b90506105f982826105b2565b919050565b600067ffffffffffffffff82111561061957610618610583565b5b61062282610572565b9050602081019050919050565b82818337600083830152505050565b600061065161064c846105fe565b6105e3565b90508281526020810184848401111561066d5761066c61056d565b5b61067884828561062f565b509392505050565b600082601f83011261069557610694610568565b5b81356106a584826020860161063e565b91505092915050565b6000819050919050565b6106c1816106ae565b81146106cc57600080fd5b50565b6000813590506106de816106b8565b92915050565b600080604083850312156106fb576106fa61055e565b5b600083013567ffffffffffffffff81111561071957610718610563565b5b61072585828601610680565b9250506020610736858286016106cf565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061076b82610740565b9050919050565b61077b81610760565b811461078657600080fd5b50565b60008135905061079881610772565b92915050565b600080600080608085870312156107b8576107b761055e565b5b60006107c687828801610789565b94505060206107d7878288016106cf565b935050604085013567ffffffffffffffff8111156107f8576107f7610563565b5b61080487828801610680565b9250506060610815878288016106cf565b91505092959194509250565b600061082c82610740565b9050919050565b61083c81610821565b82525050565b61084b816106ae565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561088b578082015181840152602081019050610870565b60008484015250505050565b60006108a282610851565b6108ac818561085c565b93506108bc81856020860161086d565b6108c581610572565b840191505092915050565b60006060820190506108e56000830186610833565b6108f26020830185610842565b81810360408301526109048184610897565b9050949350505050565b60008151905061091d816106b8565b92915050565b6000602082840312156109395761093861055e565b5b60006109478482850161090e565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e73656e64546f42616e6b20737563636565646564206260008201527f7574207472616e73666572726564207468652077726f6e6720616d6f756e7400602082015250565b60006109b7603f83610950565b91506109c28261095b565b603f82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b60006109fe82610851565b610a088185610950565b9350610a1881856020860161086d565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b6000610a55826109aa565b9150610a60826109cd565b600b82019150610a7082856109f3565b9150610a7b82610a24565b600982019150610a8b82846109f3565b91508190509392505050565b60006020820190508181036000830152610ab18184610897565b905092915050565b7f4661696c656420746f2073656e64206e617469766520746f6b656e0000000000600082015250565b6000610aef601b8361085c565b9150610afa82610ab9565b602082019050919050565b60006020820190508181036000830152610b1e81610ae2565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220ac863296eb3e4f13263954a8f80a733ab9593af91607e47e603b2f04399c60c964736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestPrecompileSelfCallRevert.sol/TestPrecompileSelfCallRevert.json b/x/evm/embeds/artifacts/contracts/TestPrecompileSelfCallRevert.sol/TestPrecompileSelfCallRevert.json new file mode 100644 index 000000000..4821fc3c9 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestPrecompileSelfCallRevert.sol/TestPrecompileSelfCallRevert.json @@ -0,0 +1,78 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestPrecompileSelfCallRevert", + "sourceName": "contracts/TestPrecompileSelfCallRevert.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "erc20_", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "nativeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nativeAmount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "precompileRecipient", + "type": "string" + }, + { + "internalType": "uint256", + "name": "precompileAmount", + "type": "uint256" + } + ], + "name": "selfCallTransferFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "nativeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nativeAmount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "precompileRecipient", + "type": "string" + }, + { + "internalType": "uint256", + "name": "precompileAmount", + "type": "uint256" + } + ], + "name": "transferFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040526000600155604051610c8e380380610c8e833981810160405281019061002a91906100d3565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610100565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a082610075565b9050919050565b6100b081610095565b81146100bb57600080fd5b50565b6000815190506100cd816100a7565b92915050565b6000602082840312156100e9576100e8610070565b5b60006100f7848285016100be565b91505092915050565b610b7f8061010f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636ad4fe961461003b578063705964db14610057575b600080fd5b610055600480360381019061005091906106c1565b610073565b005b610071600480360381019061006c91906106c1565b610208565b005b8373ffffffffffffffffffffffffffffffffffffffff166108fc849081150290604051600060405180830381858888f193505050506100e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100de906107a1565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684866040518463ffffffff1660e01b81526004016101489392919061085f565b6020604051808303816000875af1158015610167573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061018b91906108b2565b9050818114610199856102b2565b6101a2846102b2565b6040516020016101b39291906109d9565b60405160208183030381529060405290610203576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101fa9190610a26565b60405180910390fd5b600080fd5b6001600081548092919061021b90610a77565b91905055503073ffffffffffffffffffffffffffffffffffffffff16636ad4fe96858585856040518563ffffffff1660e01b815260040161025f9493929190610ace565b600060405180830381600087803b15801561027957600080fd5b505af192505050801561028a575060015b6102ab57600160008154809291906102a190610a77565b91905055506102ac565b5b50505050565b6060600060016102c184610380565b01905060008167ffffffffffffffff8111156102e0576102df610596565b5b6040519080825280601f01601f1916602001820160405280156103125781602001600182028036833780820191505090505b509050600082602001820190505b600115610375578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a858161036957610368610b1a565b5b04945060008503610320575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083106103de577a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083816103d4576103d3610b1a565b5b0492506040810190505b6d04ee2d6d415b85acef8100000000831061041b576d04ee2d6d415b85acef8100000000838161041157610410610b1a565b5b0492506020810190505b662386f26fc10000831061044a57662386f26fc1000083816104405761043f610b1a565b5b0492506010810190505b6305f5e1008310610473576305f5e100838161046957610468610b1a565b5b0492506008810190505b612710831061049857612710838161048e5761048d610b1a565b5b0492506004810190505b606483106104bb57606483816104b1576104b0610b1a565b5b0492506002810190505b600a83106104ca576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610512826104e7565b9050919050565b61052281610507565b811461052d57600080fd5b50565b60008135905061053f81610519565b92915050565b6000819050919050565b61055881610545565b811461056357600080fd5b50565b6000813590506105758161054f565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6105ce82610585565b810181811067ffffffffffffffff821117156105ed576105ec610596565b5b80604052505050565b60006106006104d3565b905061060c82826105c5565b919050565b600067ffffffffffffffff82111561062c5761062b610596565b5b61063582610585565b9050602081019050919050565b82818337600083830152505050565b600061066461065f84610611565b6105f6565b9050828152602081018484840111156106805761067f610580565b5b61068b848285610642565b509392505050565b600082601f8301126106a8576106a761057b565b5b81356106b8848260208601610651565b91505092915050565b600080600080608085870312156106db576106da6104dd565b5b60006106e987828801610530565b94505060206106fa87828801610566565b935050604085013567ffffffffffffffff81111561071b5761071a6104e2565b5b61072787828801610693565b925050606061073887828801610566565b91505092959194509250565b600082825260208201905092915050565b7f455448207472616e73666572206661696c656400000000000000000000000000600082015250565b600061078b601383610744565b915061079682610755565b602082019050919050565b600060208201905081810360008301526107ba8161077e565b9050919050565b60006107cc826104e7565b9050919050565b6107dc816107c1565b82525050565b6107eb81610545565b82525050565b600081519050919050565b60005b8381101561081a5780820151818401526020810190506107ff565b60008484015250505050565b6000610831826107f1565b61083b8185610744565b935061084b8185602086016107fc565b61085481610585565b840191505092915050565b600060608201905061087460008301866107d3565b61088160208301856107e2565b81810360408301526108938184610826565b9050949350505050565b6000815190506108ac8161054f565b92915050565b6000602082840312156108c8576108c76104dd565b5b60006108d68482850161089d565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e73656e64546f42616e6b20737563636565646564206260008201527f7574207472616e73666572726564207468652077726f6e6720616d6f756e7400602082015250565b6000610946603f836108df565b9150610951826108ea565b603f82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b600061098d826107f1565b61099781856108df565b93506109a78185602086016107fc565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b60006109e482610939565b91506109ef8261095c565b600b820191506109ff8285610982565b9150610a0a826109b3565b600982019150610a1a8284610982565b91508190509392505050565b60006020820190508181036000830152610a408184610826565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610a8282610545565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610ab457610ab3610a48565b5b600182019050919050565b610ac881610507565b82525050565b6000608082019050610ae36000830187610abf565b610af060208301866107e2565b8181036040830152610b028185610826565b9050610b1160608301846107e2565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220aa268c3644db7f02b6eccdfc2863b4633de6b642171506e23cd7e679d80056b864736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80636ad4fe961461003b578063705964db14610057575b600080fd5b610055600480360381019061005091906106c1565b610073565b005b610071600480360381019061006c91906106c1565b610208565b005b8373ffffffffffffffffffffffffffffffffffffffff166108fc849081150290604051600060405180830381858888f193505050506100e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100de906107a1565b60405180910390fd5b600061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1684866040518463ffffffff1660e01b81526004016101489392919061085f565b6020604051808303816000875af1158015610167573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061018b91906108b2565b9050818114610199856102b2565b6101a2846102b2565b6040516020016101b39291906109d9565b60405160208183030381529060405290610203576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101fa9190610a26565b60405180910390fd5b600080fd5b6001600081548092919061021b90610a77565b91905055503073ffffffffffffffffffffffffffffffffffffffff16636ad4fe96858585856040518563ffffffff1660e01b815260040161025f9493929190610ace565b600060405180830381600087803b15801561027957600080fd5b505af192505050801561028a575060015b6102ab57600160008154809291906102a190610a77565b91905055506102ac565b5b50505050565b6060600060016102c184610380565b01905060008167ffffffffffffffff8111156102e0576102df610596565b5b6040519080825280601f01601f1916602001820160405280156103125781602001600182028036833780820191505090505b509050600082602001820190505b600115610375578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a858161036957610368610b1a565b5b04945060008503610320575b819350505050919050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083106103de577a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000083816103d4576103d3610b1a565b5b0492506040810190505b6d04ee2d6d415b85acef8100000000831061041b576d04ee2d6d415b85acef8100000000838161041157610410610b1a565b5b0492506020810190505b662386f26fc10000831061044a57662386f26fc1000083816104405761043f610b1a565b5b0492506010810190505b6305f5e1008310610473576305f5e100838161046957610468610b1a565b5b0492506008810190505b612710831061049857612710838161048e5761048d610b1a565b5b0492506004810190505b606483106104bb57606483816104b1576104b0610b1a565b5b0492506002810190505b600a83106104ca576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610512826104e7565b9050919050565b61052281610507565b811461052d57600080fd5b50565b60008135905061053f81610519565b92915050565b6000819050919050565b61055881610545565b811461056357600080fd5b50565b6000813590506105758161054f565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6105ce82610585565b810181811067ffffffffffffffff821117156105ed576105ec610596565b5b80604052505050565b60006106006104d3565b905061060c82826105c5565b919050565b600067ffffffffffffffff82111561062c5761062b610596565b5b61063582610585565b9050602081019050919050565b82818337600083830152505050565b600061066461065f84610611565b6105f6565b9050828152602081018484840111156106805761067f610580565b5b61068b848285610642565b509392505050565b600082601f8301126106a8576106a761057b565b5b81356106b8848260208601610651565b91505092915050565b600080600080608085870312156106db576106da6104dd565b5b60006106e987828801610530565b94505060206106fa87828801610566565b935050604085013567ffffffffffffffff81111561071b5761071a6104e2565b5b61072787828801610693565b925050606061073887828801610566565b91505092959194509250565b600082825260208201905092915050565b7f455448207472616e73666572206661696c656400000000000000000000000000600082015250565b600061078b601383610744565b915061079682610755565b602082019050919050565b600060208201905081810360008301526107ba8161077e565b9050919050565b60006107cc826104e7565b9050919050565b6107dc816107c1565b82525050565b6107eb81610545565b82525050565b600081519050919050565b60005b8381101561081a5780820151818401526020810190506107ff565b60008484015250505050565b6000610831826107f1565b61083b8185610744565b935061084b8185602086016107fc565b61085481610585565b840191505092915050565b600060608201905061087460008301866107d3565b61088160208301856107e2565b81810360408301526108938184610826565b9050949350505050565b6000815190506108ac8161054f565b92915050565b6000602082840312156108c8576108c76104dd565b5b60006108d68482850161089d565b91505092915050565b600081905092915050565b7f4946756e546f6b656e2e73656e64546f42616e6b20737563636565646564206260008201527f7574207472616e73666572726564207468652077726f6e6720616d6f756e7400602082015250565b6000610946603f836108df565b9150610951826108ea565b603f82019050919050565b7f73656e74416d6f756e7420000000000000000000000000000000000000000000815250565b600061098d826107f1565b61099781856108df565b93506109a78185602086016107fc565b80840191505092915050565b7f6578706563746564200000000000000000000000000000000000000000000000815250565b60006109e482610939565b91506109ef8261095c565b600b820191506109ff8285610982565b9150610a0a826109b3565b600982019150610a1a8284610982565b91508190509392505050565b60006020820190508181036000830152610a408184610826565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610a8282610545565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610ab457610ab3610a48565b5b600182019050919050565b610ac881610507565b82525050565b6000608082019050610ae36000830187610abf565b610af060208301866107e2565b8181036040830152610b028185610826565b9050610b1160608301846107e2565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220aa268c3644db7f02b6eccdfc2863b4633de6b642171506e23cd7e679d80056b864736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestPrecompileSendToBankThenERC20Transfer.sol/TestPrecompileSendToBankThenERC20Transfer.json b/x/evm/embeds/artifacts/contracts/TestPrecompileSendToBankThenERC20Transfer.sol/TestPrecompileSendToBankThenERC20Transfer.json new file mode 100644 index 000000000..ba4e68635 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestPrecompileSendToBankThenERC20Transfer.sol/TestPrecompileSendToBankThenERC20Transfer.json @@ -0,0 +1,60 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestPrecompileSendToBankThenERC20Transfer", + "sourceName": "contracts/TestPrecompileSendToBankThenERC20Transfer.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_erc20", + "type": "address" + }, + { + "internalType": "string", + "name": "_recipient", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "attack", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "erc20", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "recipient", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b5060405162000d8c38038062000d8c833981810160405281019062000037919062000289565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600190816200008891906200053a565b50505062000621565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620000d282620000a5565b9050919050565b620000e481620000c5565b8114620000f057600080fd5b50565b6000815190506200010481620000d9565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200015f8262000114565b810181811067ffffffffffffffff8211171562000181576200018062000125565b5b80604052505050565b60006200019662000091565b9050620001a4828262000154565b919050565b600067ffffffffffffffff821115620001c757620001c662000125565b5b620001d28262000114565b9050602081019050919050565b60005b83811015620001ff578082015181840152602081019050620001e2565b60008484015250505050565b6000620002226200021c84620001a9565b6200018a565b9050828152602081018484840111156200024157620002406200010f565b5b6200024e848285620001df565b509392505050565b600082601f8301126200026e576200026d6200010a565b5b8151620002808482602086016200020b565b91505092915050565b60008060408385031215620002a357620002a26200009b565b5b6000620002b385828601620000f3565b925050602083015167ffffffffffffffff811115620002d757620002d6620000a0565b5b620002e58582860162000256565b9150509250929050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200034257607f821691505b602082108103620003585762000357620002fa565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003c27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000383565b620003ce868362000383565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200041b620004156200040f84620003e6565b620003f0565b620003e6565b9050919050565b6000819050919050565b6200043783620003fa565b6200044f620004468262000422565b84845462000390565b825550505050565b600090565b6200046662000457565b620004738184846200042c565b505050565b5b818110156200049b576200048f6000826200045c565b60018101905062000479565b5050565b601f821115620004ea57620004b4816200035e565b620004bf8462000373565b81016020851015620004cf578190505b620004e7620004de8562000373565b83018262000478565b50505b505050565b600082821c905092915050565b60006200050f60001984600802620004ef565b1980831691505092915050565b60006200052a8383620004fc565b9150826002028217905092915050565b6200054582620002ef565b67ffffffffffffffff81111562000561576200056062000125565b5b6200056d825462000329565b6200057a8282856200049f565b600060209050601f831160018114620005b257600084156200059d578287015190505b620005a985826200051c565b86555062000619565b601f198416620005c2866200035e565b60005b82811015620005ec57848901518255600182019150602085019450602081019050620005c5565b868310156200060c578489015162000608601f891682620004fc565b8355505b6001600288020188555050505b505050505050565b61075b80620006316000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806366d003ac14610046578063785e9e86146100645780639e5faafc14610082575b600080fd5b61004e61008c565b60405161005b91906103b6565b60405180910390f35b61006c61011a565b6040516100799190610457565b60405180910390f35b61008a61013e565b005b60018054610099906104a1565b80601f01602080910402602001604051908101604052809291908181526020018280546100c5906104a1565b80156101125780601f106100e757610100808354040283529160200191610112565b820191906000526020600020905b8154815290600101906020018083116100f557829003601f168201915b505050505081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161019a91906104f3565b602060405180830381865afa1580156101b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101db9190610549565b905061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360016040518463ffffffff1660e01b815260040161023d9392919061061e565b6020604051808303816000875af115801561025c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102809190610549565b5060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb61dead60016040518363ffffffff1660e01b81526004016102df929190610697565b6020604051808303816000875af11580156102fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061032291906106f8565b5050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610360578082015181840152602081019050610345565b60008484015250505050565b6000601f19601f8301169050919050565b600061038882610326565b6103928185610331565b93506103a2818560208601610342565b6103ab8161036c565b840191505092915050565b600060208201905081810360008301526103d0818461037d565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061041d610418610413846103d8565b6103f8565b6103d8565b9050919050565b600061042f82610402565b9050919050565b600061044182610424565b9050919050565b61045181610436565b82525050565b600060208201905061046c6000830184610448565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806104b957607f821691505b6020821081036104cc576104cb610472565b5b50919050565b60006104dd826103d8565b9050919050565b6104ed816104d2565b82525050565b600060208201905061050860008301846104e4565b92915050565b600080fd5b6000819050919050565b61052681610513565b811461053157600080fd5b50565b6000815190506105438161051d565b92915050565b60006020828403121561055f5761055e61050e565b5b600061056d84828501610534565b91505092915050565b61057f81610513565b82525050565b60008190508160005260206000209050919050565b600081546105a7816104a1565b6105b18186610331565b945060018216600081146105cc57600181146105e257610615565b60ff198316865281151560200286019350610615565b6105eb85610585565b60005b8381101561060d578154818901526001820191506020810190506105ee565b808801955050505b50505092915050565b600060608201905061063360008301866104e4565b6106406020830185610576565b8181036040830152610652818461059a565b9050949350505050565b6000819050919050565b600061068161067c6106778461065c565b6103f8565b610513565b9050919050565b61069181610666565b82525050565b60006040820190506106ac60008301856104e4565b6106b96020830184610688565b9392505050565b60008115159050919050565b6106d5816106c0565b81146106e057600080fd5b50565b6000815190506106f2816106cc565b92915050565b60006020828403121561070e5761070d61050e565b5b600061071c848285016106e3565b9150509291505056fea26469706673582212208ef08251d818031ab4c74d22a618b6305b406be371abda8aeb6b83c46f6c195a64736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c806366d003ac14610046578063785e9e86146100645780639e5faafc14610082575b600080fd5b61004e61008c565b60405161005b91906103b6565b60405180910390f35b61006c61011a565b6040516100799190610457565b60405180910390f35b61008a61013e565b005b60018054610099906104a1565b80601f01602080910402602001604051908101604052809291908181526020018280546100c5906104a1565b80156101125780601f106100e757610100808354040283529160200191610112565b820191906000526020600020905b8154815290600101906020018083116100f557829003601f168201915b505050505081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b815260040161019a91906104f3565b602060405180830381865afa1580156101b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101db9190610549565b905061080073ffffffffffffffffffffffffffffffffffffffff1663e77a47bf60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff168360016040518463ffffffff1660e01b815260040161023d9392919061061e565b6020604051808303816000875af115801561025c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102809190610549565b5060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a9059cbb61dead60016040518363ffffffff1660e01b81526004016102df929190610697565b6020604051808303816000875af11580156102fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061032291906106f8565b5050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610360578082015181840152602081019050610345565b60008484015250505050565b6000601f19601f8301169050919050565b600061038882610326565b6103928185610331565b93506103a2818560208601610342565b6103ab8161036c565b840191505092915050565b600060208201905081810360008301526103d0818461037d565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061041d610418610413846103d8565b6103f8565b6103d8565b9050919050565b600061042f82610402565b9050919050565b600061044182610424565b9050919050565b61045181610436565b82525050565b600060208201905061046c6000830184610448565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806104b957607f821691505b6020821081036104cc576104cb610472565b5b50919050565b60006104dd826103d8565b9050919050565b6104ed816104d2565b82525050565b600060208201905061050860008301846104e4565b92915050565b600080fd5b6000819050919050565b61052681610513565b811461053157600080fd5b50565b6000815190506105438161051d565b92915050565b60006020828403121561055f5761055e61050e565b5b600061056d84828501610534565b91505092915050565b61057f81610513565b82525050565b60008190508160005260206000209050919050565b600081546105a7816104a1565b6105b18186610331565b945060018216600081146105cc57600181146105e257610615565b60ff198316865281151560200286019350610615565b6105eb85610585565b60005b8381101561060d578154818901526001820191506020810190506105ee565b808801955050505b50505092915050565b600060608201905061063360008301866104e4565b6106406020830185610576565b8181036040830152610652818461059a565b9050949350505050565b6000819050919050565b600061068161067c6106778461065c565b6103f8565b610513565b9050919050565b61069181610666565b82525050565b60006040820190506106ac60008301856104e4565b6106b96020830184610688565b9392505050565b60008115159050919050565b6106d5816106c0565b81146106e057600080fd5b50565b6000815190506106f2816106cc565b92915050565b60006020828403121561070e5761070d61050e565b5b600061071c848285016106e3565b9150509291505056fea26469706673582212208ef08251d818031ab4c74d22a618b6305b406be371abda8aeb6b83c46f6c195a64736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/TestRandom.sol/TestRandom.json b/x/evm/embeds/artifacts/contracts/TestRandom.sol/TestRandom.json new file mode 100644 index 000000000..7ce4989f4 --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/TestRandom.sol/TestRandom.json @@ -0,0 +1,24 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestRandom", + "sourceName": "contracts/TestRandom.sol", + "abi": [ + { + "inputs": [], + "name": "getRandom", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5060b58061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063aacc5a1714602d575b600080fd5b60336047565b604051603e91906066565b60405180910390f35b600044905090565b6000819050919050565b606081604f565b82525050565b6000602082019050607960008301846059565b9291505056fea264697066735822122021d2de67a73b1cbeefb199f56299f80c5f7aeb23a677b105ef78f6880f75491464736f6c63430008180033", + "deployedBytecode": "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063aacc5a1714602d575b600080fd5b60336047565b604051603e91906066565b60405180910390f35b600044905090565b6000819050919050565b606081604f565b82525050565b6000602082019050607960008301846059565b9291505056fea264697066735822122021d2de67a73b1cbeefb199f56299f80c5f7aeb23a677b105ef78f6880f75491464736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/artifacts/contracts/Wasm.sol/IWasm.json b/x/evm/embeds/artifacts/contracts/Wasm.sol/IWasm.json new file mode 100644 index 000000000..a005bfcbb --- /dev/null +++ b/x/evm/embeds/artifacts/contracts/Wasm.sol/IWasm.json @@ -0,0 +1,223 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "IWasm", + "sourceName": "contracts/Wasm.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "eventType", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "abciEvent", + "type": "string" + } + ], + "name": "AbciEvent", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "string", + "name": "denom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct INibiruEvm.BankCoin[]", + "name": "funds", + "type": "tuple[]" + } + ], + "name": "execute", + "outputs": [ + { + "internalType": "bytes", + "name": "response", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "string", + "name": "denom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct INibiruEvm.BankCoin[]", + "name": "funds", + "type": "tuple[]" + } + ], + "internalType": "struct IWasm.WasmExecuteMsg[]", + "name": "executeMsgs", + "type": "tuple[]" + } + ], + "name": "executeMulti", + "outputs": [ + { + "internalType": "bytes[]", + "name": "responses", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "admin", + "type": "string" + }, + { + "internalType": "uint64", + "name": "codeID", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "msgArgs", + "type": "bytes" + }, + { + "internalType": "string", + "name": "label", + "type": "string" + }, + { + "components": [ + { + "internalType": "string", + "name": "denom", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct INibiruEvm.BankCoin[]", + "name": "funds", + "type": "tuple[]" + } + ], + "name": "instantiate", + "outputs": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "req", + "type": "bytes" + } + ], + "name": "query", + "outputs": [ + { + "internalType": "bytes", + "name": "response", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "contractAddr", + "type": "string" + }, + { + "internalType": "bytes", + "name": "key", + "type": "bytes" + } + ], + "name": "queryRaw", + "outputs": [ + { + "internalType": "bytes", + "name": "response", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/x/evm/embeds/contracts/ERC20Minter.sol b/x/evm/embeds/contracts/ERC20Minter.sol new file mode 100644 index 000000000..de57f0700 --- /dev/null +++ b/x/evm/embeds/contracts/ERC20Minter.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.19; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +/// @dev {ERC20} token, including: +/// +/// - an "owner" that can mint tokens +/// - ability for holders to burn (destroy) their tokens +/// +/// The contract owner is set automatically in the constructor as the +/// deployer due to "Ownable". +/// +/// The Context contract is inherited indirectly through "ERC20" and "Ownable". +contract ERC20Minter is ERC20, ERC20Burnable, Ownable { + uint8 private _decimals; + + /// @dev Grants "owner" status to the account that deploys the contract and + /// customizes tokens decimals. + /// + /// See {ERC20-constructor}. + constructor( + string memory name, + string memory symbol, + uint8 decimals_ + ) ERC20(name, symbol) { + _setupDecimals(decimals_); + } + + /// @dev Sets `_decimals` as `decimals_ once at Deployment' + function _setupDecimals(uint8 decimals_) private { + _decimals = decimals_; + } + + /// @dev Overrides the `decimals()` method with custom `_decimals` + function decimals() public view virtual override returns (uint8) { + return _decimals; + } + + /// @dev Creates `amount` new tokens for `to`. + /// + /// See {ERC20-_mint}. + function mint(address to, uint256 amount) public virtual onlyOwner { + _mint(to, amount); + } + + /// @dev Destroys `amount` new tokens for `to`. Suitable when the contract owner + /// should have authority to burn tokens from an account directly, such as in + /// the case of regulatory compliance, or actions selected via + /// decentralized governance. + /// + /// See {ERC20-_burn}. + function burnFromAuthority( + address from, + uint256 amount + ) public virtual onlyOwner { + _burn(from, amount); + } +} diff --git a/x/evm/embeds/contracts/IFunToken.sol b/x/evm/embeds/contracts/IFunToken.sol new file mode 100644 index 000000000..bf753d0ae --- /dev/null +++ b/x/evm/embeds/contracts/IFunToken.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +address constant FUNTOKEN_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000800; +IFunToken constant FUNTOKEN_PRECOMPILE = IFunToken(FUNTOKEN_PRECOMPILE_ADDRESS); + +import "./NibiruEvmUtils.sol"; + +/// @notice Implements the functionality for sending ERC20 tokens and bank +/// coins to various Nibiru accounts using either the Nibiru Bech32 address +/// using the "FunToken" mapping between the ERC20 and bank. +interface IFunToken is INibiruEvm { + /// @notice sendToBank sends ERC20 tokens as coins to a Nibiru base account + /// @param erc20 - the address of the ERC20 token contract + /// @param amount - the amount of tokens to send + /// @param to - the receiving Nibiru base account address as a string + /// @return sentAmount - amount of tokens received by the recipient. This may + /// not be equal to `amount` if the corresponding ERC20 contract has a fee or + /// deduction on transfer. + function sendToBank( + address erc20, + uint256 amount, + string calldata to + ) external returns (uint256 sentAmount); + + struct NibiruAccount { + address ethAddr; + string bech32Addr; + } + struct FunToken { + address erc20; + string bankDenom; + } + + /// @notice Method "balance" returns the ERC20 balance and Bank Coin balance + /// of some fungible token held by the given account. + function balance( + address who, + address funtoken + ) + external + view + returns ( + uint256 erc20Balance, + uint256 bankBalance, + FunToken memory token, + NibiruAccount memory whoAddrs + ); + + /// @notice Method "bankBalance" returns the Bank Coin balance of some + /// fungible token held by the given account. + function bankBalance( + address who, + string calldata bankDenom + ) + external + view + returns (uint256 bankBalance, NibiruAccount memory whoAddrs); + + /// @notice Method "whoAmI" performs address resolution for the given address + /// string + /// @param who Ethereum hexadecimal (EVM) address or nibi-prefixed Bech32 + /// (non-EVM) address + /// @return whoAddrs Addresses of "who" in EVM and non-EVM formats + function whoAmI( + string calldata who + ) external view returns (NibiruAccount memory whoAddrs); + + /// @notice sendToEvm transfers the caller's Bank Coins specified by `denom` + /// to the corresponding ERC-20 representation on the EVM side. The `to` + /// argument must be either an Ethereum hex address (0x...) or a Bech32 + /// address. + /// + /// The underlying logic mints (or un-escrows) the ERC-20 tokens to the `to` address if + /// the funtoken mapping was originally minted from a coin. + /// + /// @param bankDenom The bank denom of the coin to send from the caller to the EVM side. + /// @param amount The number of coins to send. + /// @param to The Ethereum hex or bech32 address receiving the ERC-20. + /// @return sentAmount The number of ERC-20 tokens minted or un-escrowed. + function sendToEvm( + string calldata bankDenom, + uint256 amount, + string calldata to + ) external returns (uint256 sentAmount); + + /// @notice bankMsgSend performs a `cosmos.bank.v1beta1.MsgSend` transaction + /// message to transfer Bank Coin funds to the given address. + /// + /// @param to The recipient address (hex or bech32). + /// @param bankDenom The bank coin denom to send. + /// @param amount The number of coins to send. + /// @return success True if the bank send succeeded, false otherwise. + function bankMsgSend( + string calldata to, + string calldata bankDenom, + uint256 amount + ) external returns (bool success); +} diff --git a/x/evm/embeds/contracts/IOracle.sol b/x/evm/embeds/contracts/IOracle.sol new file mode 100644 index 000000000..a4ed075e2 --- /dev/null +++ b/x/evm/embeds/contracts/IOracle.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +/// @notice Oracle interface for querying exchange rates +interface IOracle { + /// @notice Queries the dated exchange rate for a given pair + /// @param pair The asset pair to query. For example, "ubtc:uusd" is the + /// USD price of BTC and "unibi:uusd" is the USD price of NIBI. + /// @return price The exchange rate for the given pair + /// @return blockTimeMs The block time in milliseconds when the price was + /// last updated + /// @return blockHeight The block height when the price was last updated + /// @dev This function is view-only and does not modify state. + function queryExchangeRate( + string memory pair + ) + external + view + returns (uint256 price, uint64 blockTimeMs, uint64 blockHeight); + + function chainLinkLatestRoundData( + string memory pair + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); +} + +address constant ORACLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; + +IOracle constant NIBIRU_ORACLE = IOracle(ORACLE_PRECOMPILE_ADDRESS); + +// ChainLink interface from: +// import "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol"; +// solhint-disable-next-line interface-starts-with-i +interface ChainLinkAggregatorV3Interface { + function decimals() external view returns (uint8); + + function description() external view returns (string memory); + + function version() external view returns (uint256); + + function getRoundData( + uint80 _roundId + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); +} diff --git a/x/evm/embeds/contracts/MKR.sol b/x/evm/embeds/contracts/MKR.sol new file mode 100644 index 000000000..a7d11270e --- /dev/null +++ b/x/evm/embeds/contracts/MKR.sol @@ -0,0 +1,478 @@ +/** + *Submitted for verification at Etherscan.io on 2017-11-25 +*/ + +// MKR Token + +// hevm: flattened sources of src/mkr-499.sol +pragma solidity ^0.4.15; + +////// lib/ds-roles/lib/ds-auth/src/auth.sol +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* pragma solidity ^0.4.13; */ + +contract DSAuthority { + function canCall( + address src, address dst, bytes4 sig + ) public view returns (bool); +} + +contract DSAuthEvents { + event LogSetAuthority (address indexed authority); + event LogSetOwner (address indexed owner); +} + +contract DSAuth is DSAuthEvents { + DSAuthority public authority; + address public owner; + + function DSAuth() public { + owner = msg.sender; + LogSetOwner(msg.sender); + } + + function setOwner(address owner_) + public + auth + { + owner = owner_; + LogSetOwner(owner); + } + + function setAuthority(DSAuthority authority_) + public + auth + { + authority = authority_; + LogSetAuthority(authority); + } + + modifier auth { + require(isAuthorized(msg.sender, msg.sig)); + _; + } + + function isAuthorized(address src, bytes4 sig) internal view returns (bool) { + if (src == address(this)) { + return true; + } else if (src == owner) { + return true; + } else if (authority == DSAuthority(0)) { + return false; + } else { + return authority.canCall(src, this, sig); + } + } +} + +////// lib/ds-thing/lib/ds-math/src/math.sol +/// math.sol -- mixin for inline numerical wizardry + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* pragma solidity ^0.4.13; */ + +contract DSMath { + function add(uint x, uint y) internal pure returns (uint z) { + require((z = x + y) >= x); + } + function sub(uint x, uint y) internal pure returns (uint z) { + require((z = x - y) <= x); + } + function mul(uint x, uint y) internal pure returns (uint z) { + require(y == 0 || (z = x * y) / y == x); + } + + function min(uint x, uint y) internal pure returns (uint z) { + return x <= y ? x : y; + } + function max(uint x, uint y) internal pure returns (uint z) { + return x >= y ? x : y; + } + function imin(int x, int y) internal pure returns (int z) { + return x <= y ? x : y; + } + function imax(int x, int y) internal pure returns (int z) { + return x >= y ? x : y; + } + + uint constant WAD = 10 ** 18; + uint constant RAY = 10 ** 27; + + function wmul(uint x, uint y) internal pure returns (uint z) { + z = add(mul(x, y), WAD / 2) / WAD; + } + function rmul(uint x, uint y) internal pure returns (uint z) { + z = add(mul(x, y), RAY / 2) / RAY; + } + function wdiv(uint x, uint y) internal pure returns (uint z) { + z = add(mul(x, WAD), y / 2) / y; + } + function rdiv(uint x, uint y) internal pure returns (uint z) { + z = add(mul(x, RAY), y / 2) / y; + } + + // This famous algorithm is called "exponentiation by squaring" + // and calculates x^n with x as fixed-point and n as regular unsigned. + // + // It's O(log n), instead of O(n) for naive repeated multiplication. + // + // These facts are why it works: + // + // If n is even, then x^n = (x^2)^(n/2). + // If n is odd, then x^n = x * x^(n-1), + // and applying the equation for even x gives + // x^n = x * (x^2)^((n-1) / 2). + // + // Also, EVM division is flooring and + // floor[(n-1) / 2] = floor[n / 2]. + // + function rpow(uint x, uint n) internal pure returns (uint z) { + z = n % 2 != 0 ? x : RAY; + + for (n /= 2; n != 0; n /= 2) { + x = rmul(x, x); + + if (n % 2 != 0) { + z = rmul(z, x); + } + } + } +} + +////// lib/ds-thing/lib/ds-note/src/note.sol +/// note.sol -- the `note' modifier, for logging calls as events + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* pragma solidity ^0.4.13; */ + +contract DSNote { + event LogNote( + bytes4 indexed sig, + address indexed guy, + bytes32 indexed foo, + bytes32 indexed bar, + uint wad, + bytes fax + ) anonymous; + + modifier note { + bytes32 foo; + bytes32 bar; + + assembly { + foo := calldataload(4) + bar := calldataload(36) + } + + LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data); + + _; + } +} + +////// lib/ds-thing/src/thing.sol +// thing.sol - `auth` with handy mixins. your things should be DSThings + +// Copyright (C) 2017 DappHub, LLC + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* pragma solidity ^0.4.13; */ + +/* import 'ds-auth/auth.sol'; */ +/* import 'ds-note/note.sol'; */ +/* import 'ds-math/math.sol'; */ + +contract DSThing is DSAuth, DSNote, DSMath { +} + +////// lib/ds-token/lib/ds-stop/src/stop.sol +/// stop.sol -- mixin for enable/disable functionality + +// Copyright (C) 2017 DappHub, LLC + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* pragma solidity ^0.4.13; */ + +/* import "ds-auth/auth.sol"; */ +/* import "ds-note/note.sol"; */ + +contract DSStop is DSNote, DSAuth { + + bool public stopped; + + modifier stoppable { + require(!stopped); + _; + } + function stop() public auth note { + stopped = true; + } + function start() public auth note { + stopped = false; + } + +} + +////// lib/ds-token/lib/erc20/src/erc20.sol +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* pragma solidity ^0.4.8; */ + +// Token standard API +// https://github.com/ethereum/EIPs/issues/20 + +contract ERC20 { + function totalSupply() public view returns (uint supply); + function balanceOf( address who ) public view returns (uint value); + function allowance( address owner, address spender ) public view returns (uint _allowance); + + function transfer( address to, uint value) public returns (bool ok); + function transferFrom( address from, address to, uint value) public returns (bool ok); + function approve( address spender, uint value ) public returns (bool ok); + + event Transfer( address indexed from, address indexed to, uint value); + event Approval( address indexed owner, address indexed spender, uint value); +} + +////// lib/ds-token/src/base.sol +/// base.sol -- basic ERC20 implementation + +// Copyright (C) 2015, 2016, 2017 DappHub, LLC + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* pragma solidity ^0.4.13; */ + +/* import "erc20/erc20.sol"; */ +/* import "ds-math/math.sol"; */ + +contract DSTokenBase is ERC20, DSMath { + uint256 _supply; + mapping (address => uint256) _balances; + mapping (address => mapping (address => uint256)) _approvals; + + function DSTokenBase(uint supply) public { + _balances[msg.sender] = supply; + _supply = supply; + } + + function totalSupply() public view returns (uint) { + return _supply; + } + function balanceOf(address src) public view returns (uint) { + return _balances[src]; + } + function allowance(address src, address guy) public view returns (uint) { + return _approvals[src][guy]; + } + + function transfer(address dst, uint wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + if (src != msg.sender) { + _approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad); + } + + _balances[src] = sub(_balances[src], wad); + _balances[dst] = add(_balances[dst], wad); + + Transfer(src, dst, wad); + + return true; + } + + function approve(address guy, uint wad) public returns (bool) { + _approvals[msg.sender][guy] = wad; + + Approval(msg.sender, guy, wad); + + return true; + } +} + +////// lib/ds-token/src/token.sol +/// token.sol -- ERC20 implementation with minting and burning + +// Copyright (C) 2015, 2016, 2017 DappHub, LLC + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* pragma solidity ^0.4.13; */ + +/* import "ds-stop/stop.sol"; */ + +/* import "./base.sol"; */ + +contract DSToken is DSTokenBase(0), DSStop { + + bytes32 public symbol; + uint256 public decimals = 18; // standard token precision. override to customize + + function DSToken(bytes32 symbol_) public { + symbol = symbol_; + } + + event Mint(address indexed guy, uint wad); + event Burn(address indexed guy, uint wad); + + function approve(address guy) public stoppable returns (bool) { + return super.approve(guy, uint(-1)); + } + + function approve(address guy, uint wad) public stoppable returns (bool) { + return super.approve(guy, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + stoppable + returns (bool) + { + if (src != msg.sender && _approvals[src][msg.sender] != uint(-1)) { + _approvals[src][msg.sender] = sub(_approvals[src][msg.sender], wad); + } + + _balances[src] = sub(_balances[src], wad); + _balances[dst] = add(_balances[dst], wad); + + Transfer(src, dst, wad); + + return true; + } + + function push(address dst, uint wad) public { + transferFrom(msg.sender, dst, wad); + } + function pull(address src, uint wad) public { + transferFrom(src, msg.sender, wad); + } + function move(address src, address dst, uint wad) public { + transferFrom(src, dst, wad); + } + + function mint(uint wad) public { + mint(msg.sender, wad); + } + function burn(uint wad) public { + burn(msg.sender, wad); + } + function mint(address guy, uint wad) public auth stoppable { + _balances[guy] = add(_balances[guy], wad); + _supply = add(_supply, wad); + Mint(guy, wad); + } + function burn(address guy, uint wad) public auth stoppable { + if (guy != msg.sender && _approvals[guy][msg.sender] != uint(-1)) { + _approvals[guy][msg.sender] = sub(_approvals[guy][msg.sender], wad); + } + + _balances[guy] = sub(_balances[guy], wad); + _supply = sub(_supply, wad); + Burn(guy, wad); + } + + // Optional token name + bytes32 public name = ""; + + function setName(bytes32 name_) public auth { + name = name_; + } +} \ No newline at end of file diff --git a/x/evm/embeds/contracts/NibiruEvmUtils.sol b/x/evm/embeds/contracts/NibiruEvmUtils.sol new file mode 100644 index 000000000..6a3a5b524 --- /dev/null +++ b/x/evm/embeds/contracts/NibiruEvmUtils.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +/// @notice Interface defining the AbciEvent for interoperability between +/// Ethereum and the ABCI (Application Blockchain Interface). +interface INibiruEvm { + struct BankCoin { + string denom; + uint256 amount; + } + + /// @notice Event emitted to in precompiled contracts to relay information + /// from the ABCI to the EVM logs and indexers. Consumers of this event should + /// decode the `attrs` parameter based on the `eventType` context. + /// + /// @param eventType An identifier type of the event, used for indexing. + /// Event types indexable with CometBFT indexer are in snake case like + /// "pending_ethereum_tx" or "message", while protobuf typed events use the + /// proto message name as their event type (e.g. + /// "eth.evm.v1.EventEthereumTx"). + /// @param abciEvent JSON object string with the event type and fields of an + /// ABCI event. + event AbciEvent(string indexed eventType, string abciEvent); +} diff --git a/x/evm/embeds/contracts/NibiruOracleChainLinkLike.sol b/x/evm/embeds/contracts/NibiruOracleChainLinkLike.sol new file mode 100644 index 000000000..e723d7b6b --- /dev/null +++ b/x/evm/embeds/contracts/NibiruOracleChainLinkLike.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import "./IOracle.sol"; + +/// @title NibiruOracleChainLinkLike +/// @notice This contract serves as a ChainLink-like data feed that sources its +/// "answer" value from the Nibiru Oracle system. The Nibiru Oracle gives price +/// data with 18 decimals universally, and that 18-decimal answer is scaled to +/// have the number of decimals specified by "decimals()". This is set at the +/// time of deployment. +/// _ _ _____ ____ _____ _____ _ _ +/// | \ | ||_ _|| _ \|_ _|| __ \ | | | | +/// | \| | | | | |_) | | | | |__) || | | | +/// | . ` | | | | _ < | | | _ / | | | | +/// | |\ | _| |_ | |_) |_| |_ | | \ \ | |__| | +/// |_| \_||_____||____/|_____||_| \_\ \____/ +/// +contract NibiruOracleChainLinkLike is ChainLinkAggregatorV3Interface { + string public pair; + uint8 public _decimals; + + constructor(string memory _pair, uint8 _dec) { + require(_dec <= 18, "Decimals cannot exceed 18"); + require(bytes(_pair).length > 0, "Pair string cannot be empty"); + pair = _pair; + _decimals = _dec; + } + + function decimals() external view override returns (uint8) { + return _decimals; + } + + /// @notice Returns a human-readable description of the oracle and its data + /// feed identifier (pair) in the Nibiru Oracle system + function description() external view override returns (string memory) { + return + string.concat("Nibiru Oracle ChainLink-like price feed for ", pair); + } + + /// @notice Oracle version number. Hardcoded to 1. + function version() external pure override returns (uint256) { + return 1; + } + + /// @notice Returns the latest data from the Nibiru Oracle. + /// @return roundId The block number when the answer was published onchain. + /// @return answer Data feed result scaled to the precision specified by + /// "decimals()" + /// @return startedAt UNIX timestamp in seconds when "answer" was published. + /// @return updatedAt UNIX timestamp in seconds when "answer" was published. + /// @return answeredInRound The ID of the round where the answer was computed. + /// Since the Nibiru Oracle does not have ChainLink's system of voting + /// rounds, this argument is a meaningless, arbitrary constant. + function latestRoundData() + public + view + override + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + ( + uint80 _roundId, + int256 answer18Dec, + uint256 _startedAt, + uint256 _updatedAt, + uint80 _answeredInRound + ) = NIBIRU_ORACLE.chainLinkLatestRoundData(pair); + answer = scaleAnswerToDecimals(answer18Dec); + return (_roundId, answer, _startedAt, _updatedAt, _answeredInRound); + } + + /// @notice Returns the latest data from the Nibiru Oracle. Historical round + /// retrieval is not supported. This method is a duplicate of + /// "latestRoundData". + /// @return roundId The block number when the answer was published onchain. + /// @return answer Data feed result scaled to the precision specified by + /// "decimals()" + /// @return startedAt UNIX timestamp in seconds when "answer" was published. + /// @return updatedAt UNIX timestamp in seconds when "answer" was published. + /// @return answeredInRound The ID of the round where the answer was computed. + /// Since the Nibiru Oracle does not have ChainLink's system of voting + /// rounds, this argument is a meaningless, arbitrary constant. + function getRoundData( + uint80 + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + return latestRoundData(); + } + + function scaleAnswerToDecimals( + int256 answer18Dec + ) internal view returns (int256 answer) { + // Default answers are in 18 decimals. + // Scale down to the decimals specified in the constructor. + uint8 pow10 = 18 - _decimals; + return answer18Dec / int256(10 ** pow10); + } +} diff --git a/x/evm/embeds/contracts/TestDirtyStateAttack4.sol b/x/evm/embeds/contracts/TestDirtyStateAttack4.sol new file mode 100644 index 000000000..c9dc1d1df --- /dev/null +++ b/x/evm/embeds/contracts/TestDirtyStateAttack4.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +// Uncomment this line to use console.log +// import "hardhat/console.sol"; +import "./Wasm.sol"; +import "./NibiruEvmUtils.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestDirtyStateAttack4 { + uint counter = 0; + + constructor() payable {} + + function attack(string calldata wasmAddr, bytes calldata msgArgs) external { + counter++; + + INibiruEvm.BankCoin[] memory funds = new INibiruEvm.BankCoin[](1); + funds[0] = INibiruEvm.BankCoin({denom: "unibi", amount: 1e6}); // 1 NIBI + + WASM_PRECOMPILE.execute(wasmAddr, msgArgs, funds); + } + + function getCounter() external view returns (uint) { + return counter; + } +} diff --git a/x/evm/embeds/contracts/TestDirtyStateAttack5.sol b/x/evm/embeds/contracts/TestDirtyStateAttack5.sol new file mode 100644 index 000000000..443738520 --- /dev/null +++ b/x/evm/embeds/contracts/TestDirtyStateAttack5.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +// Uncomment this line to use console.log +// import "hardhat/console.sol"; +import "./Wasm.sol"; +import "./NibiruEvmUtils.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestDirtyStateAttack5 { + constructor() payable {} + + function attack(string calldata wasmAddr, bytes calldata msgArgs) external { + INibiruEvm.BankCoin[] memory funds = new INibiruEvm.BankCoin[](1); + funds[0] = INibiruEvm.BankCoin({denom: "unibi", amount: 5e6}); // 5 NIBI + + WASM_PRECOMPILE.execute(wasmAddr, msgArgs, funds); + } +} diff --git a/x/evm/embeds/contracts/TestERC20.sol b/x/evm/embeds/contracts/TestERC20.sol new file mode 100644 index 000000000..bea532843 --- /dev/null +++ b/x/evm/embeds/contracts/TestERC20.sol @@ -0,0 +1,16 @@ +// contracts/TestERC20.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestERC20 is ERC20 { + + // Define the supply of TestERC20: 1,000,000 + uint256 constant initialSupply = 1000000 * (10**18); + + // Constructor will be called on contract creation + constructor() ERC20("TestERC20", "FOO") { + _mint(msg.sender, initialSupply); + } +} diff --git a/x/evm/embeds/contracts/TestERC20MaliciousName.sol b/x/evm/embeds/contracts/TestERC20MaliciousName.sol new file mode 100644 index 000000000..e6be7270a --- /dev/null +++ b/x/evm/embeds/contracts/TestERC20MaliciousName.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestERC20MaliciousName is ERC20 { + constructor(string memory name, string memory symbol, uint8 decimals_) + ERC20(name, symbol) { + _mint(msg.sender, 1000000 * 10**18); + } + + function name() public view virtual override returns (string memory) { + string memory actualName = super.name(); + _gasIntensiveOperation(); + return actualName; + } + + // Gas-intensive operation to simulate high computational cost + function _gasIntensiveOperation() internal pure { + uint256 result = 1; + for (uint256 i = 0; i < 100000; i++) { + result = result * 2 + 1; + result = result / 2; + result = result ^ (result << 1); + result = result & 0xFFFFFFFFFFFFFFFF; + } + // The result is not used, ensuring the compiler doesn't optimize this away + assert(result != 0); + } +} \ No newline at end of file diff --git a/x/evm/embeds/contracts/TestERC20MaliciousTransfer.sol b/x/evm/embeds/contracts/TestERC20MaliciousTransfer.sol new file mode 100644 index 000000000..07fc63699 --- /dev/null +++ b/x/evm/embeds/contracts/TestERC20MaliciousTransfer.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestERC20MaliciousTransfer is ERC20 { + constructor(string memory name, string memory symbol, uint8 decimals_) + ERC20(name, symbol) { + _mint(msg.sender, 1000000 * 10**18); + } + + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _gasIntensiveOperation(); + return super.transfer(recipient, amount); + } + + // Gas-intensive operation to simulate high computational cost + function _gasIntensiveOperation() internal pure { + uint256 result = 1; + for (uint256 i = 0; i < 100000; i++) { + result = result * 2 + 1; + result = result / 2; + result = result ^ (result << 1); + result = result & 0xFFFFFFFFFFFFFFFF; + } + // The result is not used, ensuring the compiler doesn't optimize this away + assert(result != 0); + } +} \ No newline at end of file diff --git a/x/evm/embeds/contracts/TestERC20TransferThenPrecompileSend.sol b/x/evm/embeds/contracts/TestERC20TransferThenPrecompileSend.sol new file mode 100644 index 000000000..6f939448a --- /dev/null +++ b/x/evm/embeds/contracts/TestERC20TransferThenPrecompileSend.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +// Uncomment this line to use console.log +// import "hardhat/console.sol"; +import "./IFunToken.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestERC20TransferThenPrecompileSend { + address erc20; + + constructor(address erc20_) { + erc20 = erc20_; + } + + function erc20TransferThenPrecompileSend( + address payable transferRecipient, + uint256 transferAmount, + string memory precompileRecipient, + uint256 precompileAmount + ) public { + require( + ERC20(erc20).transfer(transferRecipient, transferAmount), + "ERC-20 transfer failed" + ); + + uint256 sentAmount = FUNTOKEN_PRECOMPILE.sendToBank( + erc20, + precompileAmount, + precompileRecipient + ); + + require( + sentAmount == precompileAmount, + string.concat( + "IFunToken.sendToBank succeeded but transferred the wrong amount", + "sentAmount ", + Strings.toString(sentAmount), + "expected ", + Strings.toString(precompileAmount) + ) + ); + } +} diff --git a/x/evm/embeds/contracts/TestERC20TransferWithFee.sol b/x/evm/embeds/contracts/TestERC20TransferWithFee.sol new file mode 100644 index 000000000..e70234e32 --- /dev/null +++ b/x/evm/embeds/contracts/TestERC20TransferWithFee.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestERC20TransferWithFee is ERC20 { + uint256 constant FEE_PERCENTAGE = 10; + + constructor(string memory name, string memory symbol) + ERC20(name, symbol) { + _mint(msg.sender, 1000); + } + + function transfer(address to, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + require(amount > 0, "Transfer amount must be greater than zero"); + + uint256 fee = (amount * FEE_PERCENTAGE) / 100; + uint256 recipientAmount = amount - fee; + + _transfer(owner, address(this), fee); + _transfer(owner, to, recipientAmount); + + return true; + } +} diff --git a/x/evm/embeds/contracts/TestFunTokenPrecompileLocalGas.sol b/x/evm/embeds/contracts/TestFunTokenPrecompileLocalGas.sol new file mode 100644 index 000000000..131d509a2 --- /dev/null +++ b/x/evm/embeds/contracts/TestFunTokenPrecompileLocalGas.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./IFunToken.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract TestFunTokenPrecompileLocalGas { + address erc20; + + constructor(address erc20_) { + erc20 = erc20_; + } + + // Calls sendToBank of the FunToken Precompile with the default gas. + // Internal call could use all the gas for the parent call. + function callBankSend( + uint256 amount, + string memory bech32Recipient + ) public { + uint256 sentAmount = FUNTOKEN_PRECOMPILE.sendToBank( + erc20, + amount, + bech32Recipient + ); + require( + sentAmount == amount, + string.concat( + "IFunToken.sendToBank succeeded but transferred the wrong amount", + "sentAmount ", + Strings.toString(sentAmount), + "expected ", + Strings.toString(amount) + ) + ); + } + + // Calls sendToBank of the FunToken Precompile with the gas amount set in parameter. + // Internal call should fail if the gas provided is insufficient. + function callBankSendLocalGas( + uint256 amount, + string memory bech32Recipient, + uint256 customGas + ) public { + uint256 sentAmount = FUNTOKEN_PRECOMPILE.sendToBank{gas: customGas}( + erc20, + amount, + bech32Recipient + ); + require( + sentAmount == amount, + string.concat( + "IFunToken.sendToBank succeeded but transferred the wrong amount", + "sentAmount ", + Strings.toString(sentAmount), + "expected ", + Strings.toString(amount) + ) + ); + } +} diff --git a/x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol b/x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol new file mode 100644 index 000000000..0917f74db --- /dev/null +++ b/x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./IFunToken.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract TestInfiniteRecursionERC20 is ERC20 { + constructor(string memory name, string memory symbol, uint8 decimals_) + ERC20(name, symbol) { + _mint(msg.sender, 1000000 * 10**18); + } + + function balanceOf(address who) public view virtual override returns (uint256) { + // recurse through funtoken.balance(who, address(this)) + address(FUNTOKEN_PRECOMPILE_ADDRESS).staticcall( + abi.encodeWithSignature( + "balance(address,address)", + who, + address(this)) + ); + return 0; + } + + function transfer(address to, uint256 amount) public override returns (bool) { + // recurse through funtoken sendToBank + FUNTOKEN_PRECOMPILE.sendToBank( + address(this), + amount, + "nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl" // does not matter, it's not reached + ); + return true; + } + + function attackBalance() public { + balanceOf(address(0)); + } + + function attackTransfer() public { + transfer(address(0), 1); + } +} diff --git a/x/evm/embeds/contracts/TestNativeSendThenPrecompileSend.sol b/x/evm/embeds/contracts/TestNativeSendThenPrecompileSend.sol new file mode 100644 index 000000000..07afd3293 --- /dev/null +++ b/x/evm/embeds/contracts/TestNativeSendThenPrecompileSend.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "./IFunToken.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract TestNativeSendThenPrecompileSend { + address erc20; + + constructor(address erc20_) { + erc20 = erc20_; + } + + function nativeSendThenPrecompileSend( + address payable nativeRecipient, + uint256 nativeAmount, + string memory precompileRecipient, + uint256 precompileAmount + ) public { + bool isSent = nativeRecipient.send(nativeAmount); + require(isSent, "Failed to send native token"); + + uint256 sentAmount = FUNTOKEN_PRECOMPILE.sendToBank( + erc20, + precompileAmount, + precompileRecipient + ); + require( + sentAmount == precompileAmount, + string.concat( + "IFunToken.sendToBank succeeded but transferred the wrong amount", + "sentAmount ", + Strings.toString(sentAmount), + "expected ", + Strings.toString(precompileAmount) + ) + ); + } + + function justPrecompileSend( + string memory precompileRecipient, + uint256 precompileAmount + ) public { + uint256 sentAmount = FUNTOKEN_PRECOMPILE.sendToBank( + erc20, + precompileAmount, + precompileRecipient + ); + require( + sentAmount == precompileAmount, + string.concat( + "IFunToken.sendToBank succeeded but transferred the wrong amount", + "sentAmount ", + Strings.toString(sentAmount), + "expected ", + Strings.toString(precompileAmount) + ) + ); + } +} diff --git a/x/evm/embeds/contracts/TestPrecompileSelfCallRevert.sol b/x/evm/embeds/contracts/TestPrecompileSelfCallRevert.sol new file mode 100644 index 000000000..9e38b0496 --- /dev/null +++ b/x/evm/embeds/contracts/TestPrecompileSelfCallRevert.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "./IFunToken.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract TestPrecompileSelfCallRevert { + address erc20; + uint counter = 0; + + constructor(address erc20_) payable { + erc20 = erc20_; + } + + function selfCallTransferFunds( + address payable nativeRecipient, + uint256 nativeAmount, + string memory precompileRecipient, + uint256 precompileAmount + ) external { + counter++; + try + TestPrecompileSelfCallRevert(payable(address(this))).transferFunds( + nativeRecipient, + nativeAmount, + precompileRecipient, + precompileAmount + ) + {} catch // [1] + { + counter++; + } + } + + function transferFunds( + address payable nativeRecipient, + uint256 nativeAmount, + string memory precompileRecipient, + uint256 precompileAmount + ) external { + require(nativeRecipient.send(nativeAmount), "ETH transfer failed"); // wei + + uint256 sentAmount = FUNTOKEN_PRECOMPILE.sendToBank( + erc20, + precompileAmount, // micro-WNIBI + precompileRecipient + ); + + require( + sentAmount == precompileAmount, + string.concat( + "IFunToken.sendToBank succeeded but transferred the wrong amount", + "sentAmount ", + Strings.toString(nativeAmount), + "expected ", + Strings.toString(precompileAmount) + ) + ); + + revert(); // [4] + } +} diff --git a/x/evm/embeds/contracts/TestPrecompileSendToBankThenERC20Transfer.sol b/x/evm/embeds/contracts/TestPrecompileSendToBankThenERC20Transfer.sol new file mode 100644 index 000000000..d54dc54a3 --- /dev/null +++ b/x/evm/embeds/contracts/TestPrecompileSendToBankThenERC20Transfer.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "./IFunToken.sol"; + +contract TestPrecompileSendToBankThenERC20Transfer { + IERC20 public erc20; + string public recipient; + + constructor(address _erc20, string memory _recipient) { + erc20 = IERC20(_erc20); + recipient = _recipient; + } + + function attack() public { + // transfer this contract's entire balance to the recipient + uint balance = erc20.balanceOf(address(this)); + // sendToBank should reduce balance to zero + FUNTOKEN_PRECOMPILE.sendToBank(address(erc20), balance, recipient); + + // this call should fail because of the balance is zero + erc20.transfer(0x000000000000000000000000000000000000dEaD, 1); + } +} diff --git a/x/evm/embeds/contracts/TestRandom.sol b/x/evm/embeds/contracts/TestRandom.sol new file mode 100644 index 000000000..3f5f33a57 --- /dev/null +++ b/x/evm/embeds/contracts/TestRandom.sol @@ -0,0 +1,10 @@ +// contracts/TestERC20.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +contract TestRandom { + + function getRandom() public view returns (uint256) { + return block.prevrandao; + } +} diff --git a/x/evm/embeds/contracts/Wasm.sol b/x/evm/embeds/contracts/Wasm.sol new file mode 100644 index 000000000..37ea785df --- /dev/null +++ b/x/evm/embeds/contracts/Wasm.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +address constant WASM_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000802; + +IWasm constant WASM_PRECOMPILE = IWasm(WASM_PRECOMPILE_ADDRESS); + +import "./NibiruEvmUtils.sol"; + +interface IWasm is INibiruEvm { + /// @notice Invoke a contract's "ExecuteMsg", which corresponds to + /// "wasm/types/MsgExecuteContract". This enables arbitrary smart contract + /// execution using the Wasm VM from the EVM. + /// @param contractAddr nibi-prefixed Bech32 address of the wasm contract + /// @param msgArgs JSON encoded wasm execute invocation + /// @param funds Optional funds to supply during the execute call. It's + /// uncommon to use this field, so you'll pass an empty array most of the time. + /// @dev The three non-struct arguments are more gas efficient than encoding a + /// single argument as a WasmExecuteMsg. + function execute( + string memory contractAddr, + bytes memory msgArgs, + INibiruEvm.BankCoin[] memory funds + ) external payable returns (bytes memory response); + + struct WasmExecuteMsg { + string contractAddr; + bytes msgArgs; + INibiruEvm.BankCoin[] funds; + } + + /// @notice Identical to "execute", except for multiple contract calls. + function executeMulti( + WasmExecuteMsg[] memory executeMsgs + ) external payable returns (bytes[] memory responses); + + /// @notice Query the public API of another contract at a known address (with + /// known ABI). + /// Implements smart query, the "WasmQuery::Smart" variant from "cosmwas_std". + /// @param contractAddr nibi-prefixed Bech32 address of the wasm contract + /// @param req JSON encoded query request + /// @return response Returns whatever type the contract returns (caller should + /// know), wrapped in a JSON encoded contract result. + function query( + string memory contractAddr, + bytes memory req + ) external view returns (bytes memory response); + + /// @notice Query the raw kv-store of the contract. + /// Implements raw query, the "WasmQuery::Raw" variant from "cosmwas_std". + /// @param contractAddr nibi-prefixed Bech32 address of the wasm contract + /// @param key contract state key. For example, a `cw_storage_plus::Item` of + /// value `Item::new("state")` creates prefix store with key, "state". + /// @return response JSON encoded, raw data stored at that key. + function queryRaw( + string memory contractAddr, + bytes memory key + ) external view returns (bytes memory response); + + /// @notice InstantiateContract creates a new smart contract instance for the + /// given code id. + function instantiate( + string memory admin, + uint64 codeID, + bytes memory msgArgs, + string memory label, + INibiruEvm.BankCoin[] memory funds + ) external payable returns (string memory contractAddr, bytes memory data); +} diff --git a/x/evm/embeds/embeds.go b/x/evm/embeds/embeds.go new file mode 100644 index 000000000..1f0763311 --- /dev/null +++ b/x/evm/embeds/embeds.go @@ -0,0 +1,227 @@ +// Package "embeds" adds access to files (smart contracts) embedded in the Go +// runtime. Go source files that import "embed" can use the //go:embed directive +// to initialize a variable of type string, \[]byte, or \[FS] with the contents +// of files read from the package directory or subdirectories at compile time. +package embeds + +import ( + // The `_ "embed"` import adds access to files embedded in the running Go + // program (smart contracts). + _ "embed" + "encoding/json" + + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" +) + +var ( + //go:embed artifacts/contracts/ERC20Minter.sol/ERC20Minter.json + erc20MinterContractJSON []byte + //go:embed artifacts/contracts/IOracle.sol/IOracle.json + oracleContractJSON []byte + //go:embed artifacts/contracts/IFunToken.sol/IFunToken.json + funtokenPrecompileJSON []byte + //go:embed artifacts/contracts/Wasm.sol/IWasm.json + wasmPrecompileJSON []byte + //go:embed artifacts/contracts/TestERC20.sol/TestERC20.json + testErc20Json []byte + //go:embed artifacts/contracts/TestERC20MaliciousName.sol/TestERC20MaliciousName.json + testErc20MaliciousNameJson []byte + //go:embed artifacts/contracts/TestERC20MaliciousTransfer.sol/TestERC20MaliciousTransfer.json + testErc20MaliciousTransferJson []byte + //go:embed artifacts/contracts/TestFunTokenPrecompileLocalGas.sol/TestFunTokenPrecompileLocalGas.json + testFunTokenPrecompileLocalGasJson []byte + //go:embed artifacts/contracts/TestERC20TransferThenPrecompileSend.sol/TestERC20TransferThenPrecompileSend.json + testERC20TransferThenPrecompileSendJson []byte + //go:embed artifacts/contracts/TestNativeSendThenPrecompileSend.sol/TestNativeSendThenPrecompileSend.json + testNativeSendThenPrecompileSendJson []byte + //go:embed artifacts/contracts/TestPrecompileSelfCallRevert.sol/TestPrecompileSelfCallRevert.json + testPrecompileSelfCallRevertJson []byte + //go:embed artifacts/contracts/TestInfiniteRecursionERC20.sol/TestInfiniteRecursionERC20.json + testInfiniteRecursionERC20Json []byte + //go:embed artifacts/contracts/TestERC20TransferWithFee.sol/TestERC20TransferWithFee.json + testERC20TransferWithFee []byte + //go:embed artifacts/contracts/TestRandom.sol/TestRandom.json + testRandom []byte + //go:embed artifacts/contracts/MKR.sol/DSToken.json + testMetadataBytes32 []byte + //go:embed artifacts/contracts/TestPrecompileSendToBankThenERC20Transfer.sol/TestPrecompileSendToBankThenERC20Transfer.json + testPrecompileSendToBankThenERC20Transfer []byte + //go:embed artifacts/contracts/TestDirtyStateAttack4.sol/TestDirtyStateAttack4.json + testDirtyStateAttack4 []byte + //go:embed artifacts/contracts/TestDirtyStateAttack5.sol/TestDirtyStateAttack5.json + testDirtyStateAttack5 []byte +) + +var ( + // Contract_ERC20Minter: The default ERC20 contract deployed during the + // creation of a `FunToken` mapping from a bank coin. + SmartContract_ERC20Minter = CompiledEvmContract{ + Name: "ERC20Minter.sol", + EmbedJSON: erc20MinterContractJSON, + } + + // SmartContract_Funtoken: Precompile contract interface for + // "IFunToken.sol". This precompile enables transfers of ERC20 tokens + // to non-EVM accounts. Only the ABI is used. + SmartContract_FunToken = CompiledEvmContract{ + Name: "IFunToken.sol", + EmbedJSON: funtokenPrecompileJSON, + } + + // SmartContract_Funtoken: Precompile contract interface for + // "Wasm.sol". This precompile enables contract invocations in the Wasm VM + // from EVM accounts. Only the ABI is used. + SmartContract_Wasm = CompiledEvmContract{ + Name: "Wasm.sol", + EmbedJSON: wasmPrecompileJSON, + } + SmartContract_Oracle = CompiledEvmContract{ + Name: "Oracle.sol", + EmbedJSON: oracleContractJSON, + } + SmartContract_TestERC20 = CompiledEvmContract{ + Name: "TestERC20.sol", + EmbedJSON: testErc20Json, + } + // SmartContract_TestERC20MaliciousName is a test contract + // which simulates malicious ERC20 behavior by adding gas intensive operation + // for function name() intended to attack funtoken creation + SmartContract_TestERC20MaliciousName = CompiledEvmContract{ + Name: "TestERC20MaliciousName.sol", + EmbedJSON: testErc20MaliciousNameJson, + } + // SmartContract_TestERC20MaliciousTransfer is a test contract + // which simulates malicious ERC20 behavior by adding gas intensive operation + // for function transfer() intended to attack funtoken conversion from erc20 to bank coin + SmartContract_TestERC20MaliciousTransfer = CompiledEvmContract{ + Name: "TestERC20MaliciousTransfer.sol", + EmbedJSON: testErc20MaliciousTransferJson, + } + // SmartContract_TestFunTokenPrecompileLocalGas is a test contract + // which allows precompile execution with custom local gas set (calling precompile within contract) + SmartContract_TestFunTokenPrecompileLocalGas = CompiledEvmContract{ + Name: "TestFunTokenPrecompileLocalGas.sol", + EmbedJSON: testFunTokenPrecompileLocalGasJson, + } + // SmartContract_TestNativeSendThenPrecompileSendJson is a test contract that + // performs two sends in a single call: a native nibi send and a precompile + // sendToBank. It tests a race condition where the state DB commit may + // overwrite the state after the precompile execution, potentially causing a + // loss of funds. + SmartContract_TestNativeSendThenPrecompileSendJson = CompiledEvmContract{ + Name: "TestNativeSendThenPrecompileSend.sol", + EmbedJSON: testNativeSendThenPrecompileSendJson, + } + // SmartContract_TestERC20TransferThenPrecompileSend is a test contract that + // performs two sends in a single call: an erc20 token transfer and a + // precompile sendToBank. It tests a race condition where the state DB commit + // may overwrite the state after the precompile execution, potentially + // causing an infinite token mint. + SmartContract_TestERC20TransferThenPrecompileSend = CompiledEvmContract{ + Name: "TestERC20TransferThenPrecompileSend.sol", + EmbedJSON: testERC20TransferThenPrecompileSendJson, + } + // SmartContract_TestPrecompileSelfCallRevert is a test contract + // that creates another instance of itself, calls the precompile method and then force reverts. + // It tests a race condition where the state DB commit + // may save the wrong state before the precompile execution, not revert it entirely, + // potentially causing an infinite mint of funds. + SmartContract_TestPrecompileSelfCallRevert = CompiledEvmContract{ + Name: "TestPrecompileSelfCallRevert.sol", + EmbedJSON: testPrecompileSelfCallRevertJson, + } + // SmartContract_TestInfiniteRecursionERC20 is a test contract + // which simulates malicious ERC20 behavior by adding infinite recursion in transfer() and balanceOf() functions + SmartContract_TestInfiniteRecursionERC20 = CompiledEvmContract{ + Name: "TestInfiniteRecursionERC20.sol", + EmbedJSON: testInfiniteRecursionERC20Json, + } + // SmartContract_TestERC20TransferWithFee is a test contract + // which simulates malicious ERC20 behavior by adding fee to the transfer() function + SmartContract_TestERC20TransferWithFee = CompiledEvmContract{ + Name: "TestERC20TransferWithFee.sol", + EmbedJSON: testERC20TransferWithFee, + } + // SmartContract_TestRandom is a test contract which tests random function + SmartContract_TestRandom = CompiledEvmContract{ + Name: "TestRandom.sol", + EmbedJSON: testRandom, + } + // SmartContract_TestBytes32Metadata is a test contract which tests contract that have bytes32 as metadata + SmartContract_TestBytes32Metadata = CompiledEvmContract{ + Name: "MKR.sol", + EmbedJSON: testMetadataBytes32, + } + // SmartContract_TestPrecompileSendToBankThenERC20Transfer is a test contract that sends to bank then calls ERC20 transfer + SmartContract_TestPrecompileSendToBankThenERC20Transfer = CompiledEvmContract{ + Name: "TestPrecompileSendToBankThenERC20Transfer.sol", + EmbedJSON: testPrecompileSendToBankThenERC20Transfer, + } + // SmartContract_TestDirtyStateAttack4 is a test contract that composes manual send and funtoken sendToBank with a reversion + SmartContract_TestDirtyStateAttack4 = CompiledEvmContract{ + Name: "TestDirtyStateAttack4.sol", + EmbedJSON: testDirtyStateAttack4, + } + // SmartContract_TestDirtyStateAttack5 is a test contract that calls a wasm contract with 5 NIBI + SmartContract_TestDirtyStateAttack5 = CompiledEvmContract{ + Name: "TestDirtyStateAttack5.sol", + EmbedJSON: testDirtyStateAttack5, + } +) + +func init() { + SmartContract_ERC20Minter.MustLoad() + SmartContract_FunToken.MustLoad() + SmartContract_Wasm.MustLoad() + SmartContract_Oracle.MustLoad() + SmartContract_TestERC20.MustLoad() + SmartContract_TestERC20MaliciousName.MustLoad() + SmartContract_TestERC20MaliciousTransfer.MustLoad() + SmartContract_TestFunTokenPrecompileLocalGas.MustLoad() + SmartContract_TestNativeSendThenPrecompileSendJson.MustLoad() + SmartContract_TestERC20TransferThenPrecompileSend.MustLoad() + SmartContract_TestPrecompileSelfCallRevert.MustLoad() + SmartContract_TestInfiniteRecursionERC20.MustLoad() + SmartContract_TestERC20TransferWithFee.MustLoad() + SmartContract_TestRandom.MustLoad() + SmartContract_TestBytes32Metadata.MustLoad() + SmartContract_TestPrecompileSendToBankThenERC20Transfer.MustLoad() + SmartContract_TestDirtyStateAttack4.MustLoad() + SmartContract_TestDirtyStateAttack5.MustLoad() +} + +type CompiledEvmContract struct { + Name string + EmbedJSON []byte + + // filled in post-load + ABI *gethabi.ABI `json:"abi"` + Bytecode []byte `json:"bytecode"` +} + +func (sc *CompiledEvmContract) MustLoad() { + if sc.EmbedJSON == nil { + panic("missing compiled contract embed") + } + + rawJsonBz := make(map[string]json.RawMessage) + err := json.Unmarshal(sc.EmbedJSON, &rawJsonBz) + if err != nil { + panic(err) + } + + abi := new(gethabi.ABI) + err = abi.UnmarshalJSON(rawJsonBz["abi"]) + if err != nil { + panic(err) + } + + var bytecodeStr string + err = json.Unmarshal(rawJsonBz["bytecode"], &bytecodeStr) + if err != nil { + panic(err) + } + sc.Bytecode = gethcommon.FromHex(bytecodeStr) + sc.ABI = abi +} diff --git a/x/evm/embeds/embeds_test.go b/x/evm/embeds/embeds_test.go new file mode 100644 index 000000000..37039e124 --- /dev/null +++ b/x/evm/embeds/embeds_test.go @@ -0,0 +1,27 @@ +package embeds_test + +import ( + _ "embed" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" +) + +func TestLoadContracts(t *testing.T) { + require.NotPanics(t, func() { + embeds.SmartContract_ERC20Minter.MustLoad() + embeds.SmartContract_FunToken.MustLoad() + embeds.SmartContract_TestERC20.MustLoad() + embeds.SmartContract_TestERC20MaliciousName.MustLoad() + embeds.SmartContract_TestERC20MaliciousTransfer.MustLoad() + embeds.SmartContract_TestFunTokenPrecompileLocalGas.MustLoad() + embeds.SmartContract_TestNativeSendThenPrecompileSendJson.MustLoad() + embeds.SmartContract_TestERC20TransferThenPrecompileSend.MustLoad() + embeds.SmartContract_TestInfiniteRecursionERC20.MustLoad() + embeds.SmartContract_TestERC20TransferWithFee.MustLoad() + embeds.SmartContract_TestRandom.MustLoad() + embeds.SmartContract_TestBytes32Metadata.MustLoad() + }) +} diff --git a/x/evm/embeds/gen-abi/main.go b/x/evm/embeds/gen-abi/main.go new file mode 100644 index 000000000..cefd8d2ea --- /dev/null +++ b/x/evm/embeds/gen-abi/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/fs" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "strings" +) + +// findRootPath returns the absolute path of the repository root +// This is retrievable with: go list -m -f {{.Dir}} +func findRootPath() (string, error) { + // rootPath, _ := exec.Command("go list -m -f {{.Dir}}").Output() + // This returns the path to the root of the project. + rootPathBz, err := exec.Command("go", "list", "-m", "-f", "{{.Dir}}").Output() + if err != nil { + return "", err + } + rootPath := strings.Trim(string(rootPathBz), "\n") + return rootPath, nil +} + +func main() { + // Define the input and output directories + rootPath, err := findRootPath() + if err != nil { + log.Fatalf("Unable to find repo root path: %s", err) + } + inputDir := path.Join(rootPath, "x/evm/embeds/artifacts/contracts/") + outputDir := path.Join(rootPath, "x/evm/embeds/abi/") + + // Ensure the output directory exists + err = os.MkdirAll(outputDir, os.ModePerm) + if err != nil { + log.Fatalf("Failed to create output directory: %v", err) + } + + // Walk through the input directory + err = filepath.Walk(inputDir, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + // Process only... + if !info.IsDir() && + filepath.Ext(path) == ".json" && // .json files that + !strings.Contains(path, ".dbg") && // are NOT "dbg" files + !strings.HasPrefix(info.Name(), "Test") { // are NOT for tests + // Read the JSON file + data, err := os.ReadFile(path) + if err != nil { + return fmt.Errorf("failed to read file %s: %v", path, err) + } + + // Parse the JSON file + var parsed map[string]interface{} + err = json.Unmarshal(data, &parsed) + if err != nil { + return fmt.Errorf("failed to parse JSON in file %s: %v", path, err) + } + + // Extract the "abi" field + abi, ok := parsed["abi"] + if !ok { + fmt.Printf("No 'abi' field found in file %s, skipping...\n", path) + return nil + } + + // Marshal the ABI field back to JSON + abiData, err := json.MarshalIndent(abi, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal 'abi' field from file %s: %v", path, err) + } + + // Create the output file name + outputFileName := strings.TrimSuffix(info.Name(), filepath.Ext(info.Name())) + ".json" + outputPath := filepath.Join(outputDir, outputFileName) + + // Write the ABI JSON to the output directory + err = os.WriteFile(outputPath, abiData, 0o644) + if err != nil { + return fmt.Errorf("failed to write ABI to file %s: %v", outputPath, err) + } + + fmt.Printf("Processed and saved ABI: %s\n", outputPath) + } + + return nil + }) + if err != nil { + log.Fatalf("Error processing files: %v", err) + } +} diff --git a/x/evm/embeds/hardhat.config.js b/x/evm/embeds/hardhat.config.js new file mode 100644 index 000000000..c0d1ee331 --- /dev/null +++ b/x/evm/embeds/hardhat.config.js @@ -0,0 +1,11 @@ +require("@nomicfoundation/hardhat-toolbox"); + +/** @type import('hardhat/config').HardhatUserConfig */ +module.exports = { + solidity: { + compilers: [ + { version: "0.4.19" }, + { version: "0.8.24" }, + ], + }, +}; diff --git a/x/evm/embeds/package-lock.json b/x/evm/embeds/package-lock.json new file mode 100644 index 000000000..eef9850fe --- /dev/null +++ b/x/evm/embeds/package-lock.json @@ -0,0 +1,7799 @@ +{ + "name": "@nibiruchain/solidity", + "version": "0.0.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@nibiruchain/solidity", + "version": "0.0.2", + "license": "MIT", + "dependencies": { + "@openzeppelin/contracts": "^4.9.0" + }, + "devDependencies": { + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "bun": "^1.1.30", + "hardhat": "^2.22.5" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT" + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@metamask/eth-sig-util/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nomicfoundation/edr": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.6.5.tgz", + "integrity": "sha512-tAqMslLP+/2b2sZP4qe9AuGxG3OkQ5gGgHE4isUuq6dUVjwCRPFhAOhpdFl+OjY5P3yEv3hmq9HjUGRa2VNjng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.6.5", + "@nomicfoundation/edr-darwin-x64": "0.6.5", + "@nomicfoundation/edr-linux-arm64-gnu": "0.6.5", + "@nomicfoundation/edr-linux-arm64-musl": "0.6.5", + "@nomicfoundation/edr-linux-x64-gnu": "0.6.5", + "@nomicfoundation/edr-linux-x64-musl": "0.6.5", + "@nomicfoundation/edr-win32-x64-msvc": "0.6.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.6.5.tgz", + "integrity": "sha512-A9zCCbbNxBpLgjS1kEJSpqxIvGGAX4cYbpDYCU2f3jVqOwaZ/NU761y1SvuCRVpOwhoCXqByN9b7HPpHi0L4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.6.5.tgz", + "integrity": "sha512-x3zBY/v3R0modR5CzlL6qMfFMdgwd6oHrWpTkuuXnPFOX8SU31qq87/230f4szM+ukGK8Hi+mNq7Ro2VF4Fj+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.6.5.tgz", + "integrity": "sha512-HGpB8f1h8ogqPHTyUpyPRKZxUk2lu061g97dOQ/W4CxevI0s/qiw5DB3U3smLvSnBHKOzYS1jkxlMeGN01ky7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.6.5.tgz", + "integrity": "sha512-ESvJM5Y9XC03fZg9KaQg3Hl+mbx7dsSkTIAndoJS7X2SyakpL9KZpOSYrDk135o8s9P9lYJdPOyiq+Sh+XoCbQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.6.5.tgz", + "integrity": "sha512-HCM1usyAR1Ew6RYf5AkMYGvHBy64cPA5NMbaeY72r0mpKaH3txiMyydcHibByOGdQ8iFLWpyUdpl1egotw+Tgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.6.5.tgz", + "integrity": "sha512-nB2uFRyczhAvWUH7NjCsIO6rHnQrof3xcCe6Mpmnzfl2PYcGyxN7iO4ZMmRcQS7R1Y670VH6+8ZBiRn8k43m7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.6.5.tgz", + "integrity": "sha512-B9QD/4DSSCFtWicO8A3BrsnitO1FPv7axB62wq5Q+qeJ50yJlTmyeGY3cw62gWItdvy2mh3fRM6L1LpnHiB77A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/ethereumjs-util": "9.0.4" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", + "dev": true, + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/hardhat-chai-matchers": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.8.tgz", + "integrity": "sha512-Z5PiCXH4xhNLASROlSUOADfhfpfhYO6D7Hn9xp8PddmHey0jq704cr6kfU8TRrQ4PUZbpfsZadPj+pCfZdjPIg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai-as-promised": "^7.1.3", + "chai-as-promised": "^7.1.1", + "deep-eql": "^4.0.1", + "ordinal": "^1.0.3" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "chai": "^4.2.0", + "ethers": "^6.1.0", + "hardhat": "^2.9.4" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.8.tgz", + "integrity": "sha512-zhOZ4hdRORls31DTOqg+GmEZM0ujly8GGIuRY7t7szEk2zW/arY1qDug/py8AEktT00v5K+b6RvbVog+va51IA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "lodash.isequal": "^4.5.0" + }, + "peerDependencies": { + "ethers": "^6.1.0", + "hardhat": "^2.0.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition/-/hardhat-ignition-0.15.9.tgz", + "integrity": "sha512-lSWqhaDOBt6gsqMadkRLvH6HdoFV1v8/bx7z+12cghaOloVwwn48CPoTH2iXXnkqilPGw8rdH5eVTE6UM+2v6Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nomicfoundation/ignition-core": "^0.15.9", + "@nomicfoundation/ignition-ui": "^0.15.9", + "chalk": "^4.0.0", + "debug": "^4.3.2", + "fs-extra": "^10.0.0", + "json5": "^2.2.3", + "prompts": "^2.4.2" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-verify": "^2.0.1", + "hardhat": "^2.18.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ignition-ethers": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ignition-ethers/-/hardhat-ignition-ethers-0.15.9.tgz", + "integrity": "sha512-9PwwgLv3z2ec3B26mK0IjiFezHFFBcBcs1qKaRu8SanARE4b7RvrfiLIy8ZXE7HaxgPt32kSsQzehhzAwAIj1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.4", + "@nomicfoundation/hardhat-ignition": "^0.15.9", + "@nomicfoundation/ignition-core": "^0.15.9", + "ethers": "^6.7.0", + "hardhat": "^2.18.0" + } + }, + "node_modules/@nomicfoundation/hardhat-network-helpers": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.12.tgz", + "integrity": "sha512-xTNQNI/9xkHvjmCJnJOTyqDSl8uq1rKb2WOVmixQxFtRd7Oa3ecO8zM0cyC2YmOK+jHB9WPZ+F/ijkHg1CoORA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ethereumjs-util": "^7.1.4" + }, + "peerDependencies": { + "hardhat": "^2.9.5" + } + }, + "node_modules/@nomicfoundation/hardhat-toolbox": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-5.0.0.tgz", + "integrity": "sha512-FnUtUC5PsakCbwiVNsqlXVIWG5JIb5CEZoSXbJUsEBun22Bivx2jhF1/q9iQbzuaGpJKFQyOhemPB2+XlEE6pQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0", + "@nomicfoundation/hardhat-network-helpers": "^1.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "@typechain/ethers-v6": "^0.5.0", + "@typechain/hardhat": "^9.0.0", + "@types/chai": "^4.2.0", + "@types/mocha": ">=9.1.0", + "@types/node": ">=18.0.0", + "chai": "^4.2.0", + "ethers": "^6.4.0", + "hardhat": "^2.11.0", + "hardhat-gas-reporter": "^1.0.8", + "solidity-coverage": "^0.8.1", + "ts-node": ">=8.0.0", + "typechain": "^8.3.0", + "typescript": ">=4.5.0" + } + }, + "node_modules/@nomicfoundation/hardhat-verify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.12.tgz", + "integrity": "sha512-Lg3Nu7DCXASQRVI/YysjuAX2z8jwOCbS0w5tz2HalWGSTZThqA0v9N0v0psHbKNqzPJa8bNOeapIVSziyJTnAg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@ethersproject/address": "^5.0.2", + "cbor": "^8.1.0", + "debug": "^4.1.1", + "lodash.clonedeep": "^4.5.0", + "picocolors": "^1.1.0", + "semver": "^6.3.0", + "table": "^6.8.0", + "undici": "^5.14.0" + }, + "peerDependencies": { + "hardhat": "^2.0.4" + } + }, + "node_modules/@nomicfoundation/ignition-core": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-core/-/ignition-core-0.15.9.tgz", + "integrity": "sha512-X8W+7UP/UQPorpHUnGvA1OdsEr/edGi8tDpNwEqzaLm83FMZVbRWdOsr3vNICHN2XMzNY/xIm18Cx7xGKL2PQw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/address": "5.6.1", + "@nomicfoundation/solidity-analyzer": "^0.1.1", + "cbor": "^9.0.0", + "debug": "^4.3.2", + "ethers": "^6.7.0", + "fs-extra": "^10.0.0", + "immer": "10.0.2", + "lodash": "4.17.21", + "ndjson": "2.0.0" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/@ethersproject/address": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.6.1.tgz", + "integrity": "sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.6.2", + "@ethersproject/bytes": "^5.6.1", + "@ethersproject/keccak256": "^5.6.1", + "@ethersproject/logger": "^5.6.0", + "@ethersproject/rlp": "^5.6.1" + } + }, + "node_modules/@nomicfoundation/ignition-core/node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@nomicfoundation/ignition-ui": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ignition-ui/-/ignition-ui-0.15.9.tgz", + "integrity": "sha512-8lzbT7gpJ5PoowPQDQilkwdyqBviUKDMoHp/5rhgnwG1bDslnCS+Lxuo6s9R2akWu9LtEL14dNyqQb6WsURTag==", + "dev": true, + "peer": true + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", + "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.2", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.2" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz", + "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz", + "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz", + "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz", + "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz", + "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz", + "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz", + "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@openzeppelin/contracts": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.6.tgz", + "integrity": "sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==", + "license": "MIT" + }, + "node_modules/@oven/bun-darwin-aarch64": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.1.30.tgz", + "integrity": "sha512-D07QioP+QXlouvIqQIS+7r2zq4lTNd6he79rhKsRQRZGFf9i3NPu87zspUpCaFEu//DZ35DYTt+5anQpAzpoxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.1.30.tgz", + "integrity": "sha512-xZ4gTehS6QwN6bsJfDycCNneKoUMaFUQhQg24bJzXS4JPDxeKg1W7PS5AE+U9apz5Dx6//+D4RwVpAPG2LXt0w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64-baseline": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.1.30.tgz", + "integrity": "sha512-1kFUCxHx7WuEbLDmqm0m2UKBd3S4Ln6qKQ4gxU4umMLFkmvDJn6PszDruFInxGKFLoTAmbXNYNVWkkG/ekt/Lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-linux-aarch64": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.1.30.tgz", + "integrity": "sha512-SfHHLlph6fptDXyyChcUkeDbEZr2ww1p2BucV6OrvzwTOPi8pVmXA4360YT8ggR/3AHPp4GO36VaD+FU2Ocbxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.1.30.tgz", + "integrity": "sha512-1mC39jQSaECytEKAZdCZmv3ZreMsp7aoxnBwmJtVd2Z7urnw17PKi4dKkZd/R+AubsNYtXtW4jeM8SEa5sUJRw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-baseline": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.1.30.tgz", + "integrity": "sha512-/b/VuNOaAYmsVk9MvfwKcCYARJPUg78hebxNyD5DSajAf3dqtUSnf7QYcq/3mxWH++N+gM7uRTrGksGS63+ZUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-windows-x64": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.1.30.tgz", + "integrity": "sha512-mdRjNtD9NIA8CiH6N1zrIVE6oAtDko/c29H1s00UA+5O/WhXhg95G8IyInD8hN3vAEz8H2lGBgLG2EGfSFxnGg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oven/bun-windows-x64-baseline": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.1.30.tgz", + "integrity": "sha512-ERQ4/ogzbFvHjpyHcnruc8bnryvDvUoiWi6vczfQ4M/idJc+Kg5VSEJiF5k7946rIZGamG6QWgRxtpIglD4/Zw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@scure/base": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", + "integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@solidity-parser/parser": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", + "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@typechain/ethers-v6": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", + "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + }, + "peerDependencies": { + "ethers": "6.x", + "typechain": "^8.3.2", + "typescript": ">=4.7.0" + } + }, + "node_modules/@typechain/hardhat": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-9.1.0.tgz", + "integrity": "sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fs-extra": "^9.1.0" + }, + "peerDependencies": { + "@typechain/ethers-v6": "^0.5.1", + "ethers": "^6.1.0", + "hardhat": "^2.9.9", + "typechain": "^8.3.2" + } + }, + "node_modules/@typechain/hardhat/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.17", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.17.tgz", + "integrity": "sha512-zmZ21EWzR71B4Sscphjief5djsLre50M6lI622OSySTmn9DB3j+C3kWroHfBQWXbOBwbgg/M8CG/hUxDLIloow==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/mocha": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", + "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/node": { + "version": "22.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.2.tgz", + "integrity": "sha512-yPL6DyFwY5PiMVEwymNeqUTKsDczQBJ/5T7W/46RwLU/VH+AA8aT5TZkvBviLKLbbm0hlfftEkGrNzfRk/fofQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.11.1" + } + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "license": "BSD-3-Clause OR MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/axios": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "dev": true, + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base-x": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", + "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bun": { + "version": "1.1.30", + "resolved": "https://registry.npmjs.org/bun/-/bun-1.1.30.tgz", + "integrity": "sha512-ysRL1pq10Xba0jqVLPrKU3YIv0ohfp3cTajCPtpjCyppbn3lfiAVNpGoHfyaxS17OlPmWmR67UZRPw/EueQuug==", + "cpu": [ + "arm64", + "x64" + ], + "dev": true, + "hasInstallScript": true, + "os": [ + "darwin", + "linux", + "win32" + ], + "bin": { + "bun": "bin/bun.exe", + "bunx": "bin/bun.exe" + }, + "optionalDependencies": { + "@oven/bun-darwin-aarch64": "1.1.30", + "@oven/bun-darwin-x64": "1.1.30", + "@oven/bun-darwin-x64-baseline": "1.1.30", + "@oven/bun-linux-aarch64": "1.1.30", + "@oven/bun-linux-x64": "1.1.30", + "@oven/bun-linux-x64-baseline": "1.1.30", + "@oven/bun-windows-x64": "1.1.30", + "@oven/bun-windows-x64-baseline": "1.1.30" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "dev": true, + "license": "WTFPL", + "peer": true, + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cli-table3/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/command-line-usage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/command-line-usage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/command-line-usage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/command-line-usage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", + "dev": true, + "peer": true + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/difflib": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", + "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", + "dev": true, + "peer": true, + "dependencies": { + "heap": ">= 0.2.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eth-gas-reporter": { + "version": "0.2.27", + "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz", + "integrity": "sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@solidity-parser/parser": "^0.14.0", + "axios": "^1.5.1", + "cli-table3": "^0.5.0", + "colors": "1.4.0", + "ethereum-cryptography": "^1.0.3", + "ethers": "^5.7.2", + "fs-readdir-recursive": "^1.1.0", + "lodash": "^4.17.14", + "markdown-table": "^1.1.3", + "mocha": "^10.2.0", + "req-cwd": "^2.0.0", + "sha1": "^1.1.1", + "sync-request": "^6.0.0" + }, + "peerDependencies": { + "@codechecks/client": "^0.1.0" + }, + "peerDependenciesMeta": { + "@codechecks/client": { + "optional": true + } + } + }, + "node_modules/eth-gas-reporter/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/eth-gas-reporter/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, + "node_modules/ethereum-bloom-filters": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.2.0.tgz", + "integrity": "sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "^1.4.0" + } + }, + "node_modules/ethereum-bloom-filters/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ethereumjs-abi/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethers": { + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz", + "integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", + "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "chalk": "^2.4.2", + "node-emoji": "^1.10.0" + }, + "bin": { + "testrpc-sc": "index.js" + } + }, + "node_modules/ghost-testrpc/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ghost-testrpc/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ghost-testrpc/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ghost-testrpc/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hardhat": { + "version": "2.22.17", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.17.tgz", + "integrity": "sha512-tDlI475ccz4d/dajnADUTRc1OJ3H8fpP9sWhXhBPpYsQOg8JHq5xrDimo53UhWPl7KJmAeDCm1bFG74xvpGRpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/edr": "^0.6.5", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-tx": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "^5.1.0", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", + "chokidar": "^4.0.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^5.0.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "json-stream-stringify": "^3.1.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "picocolors": "^1.1.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.8.26", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.6", + "tsort": "0.0.1", + "undici": "^5.14.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/bootstrap.js" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat-gas-reporter": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.10.tgz", + "integrity": "sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-uniq": "1.0.3", + "eth-gas-reporter": "^0.2.25", + "sha1": "^1.1.1" + }, + "peerDependencies": { + "hardhat": "^2.0.2" + } + }, + "node_modules/hardhat/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/hardhat/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/hardhat/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/hardhat/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/hardhat/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/hardhat/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/hardhat/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/hardhat/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.2.tgz", + "integrity": "sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-stream-stringify": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", + "integrity": "sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=7.10.1" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/markdown-table": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", + "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/ndjson": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz", + "integrity": "sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "json-stringify-safe": "^5.0.1", + "minimist": "^1.2.5", + "readable-stream": "^3.6.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "ndjson": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ordinal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", + "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true, + "peer": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "peer": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/req-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", + "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "req-from": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/req-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", + "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sc-istanbul": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", + "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/sc-istanbul/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/sc-istanbul/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sc-istanbul/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sc-istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/sc-istanbul/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true, + "license": "MIT" + }, + "node_modules/secp256k1": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.7", + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/secp256k1/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/secp256k1/node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/secp256k1/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/solc": { + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "^8.1.0", + "follow-redirects": "^1.12.1", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solc.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solidity-coverage": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.14.tgz", + "integrity": "sha512-ItAAObe5GaEOp20kXC2BZRnph+9P7Rtoqg2mQc2SXGEHgSDF2wWd1Wxz3ntzQWXkbCtIIGdJT918HG00cObwbA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.0.9", + "@solidity-parser/parser": "^0.19.0", + "chalk": "^2.4.2", + "death": "^1.1.0", + "difflib": "^0.2.4", + "fs-extra": "^8.1.0", + "ghost-testrpc": "^0.0.2", + "global-modules": "^2.0.0", + "globby": "^10.0.1", + "jsonschema": "^1.2.4", + "lodash": "^4.17.21", + "mocha": "^10.2.0", + "node-emoji": "^1.10.0", + "pify": "^4.0.1", + "recursive-readdir": "^2.2.2", + "sc-istanbul": "^0.4.5", + "semver": "^7.3.4", + "shelljs": "^0.8.3", + "web3-utils": "^1.3.6" + }, + "bin": { + "solidity-coverage": "plugins/bin.js" + }, + "peerDependencies": { + "hardhat": "^2.11.0" + } + }, + "node_modules/solidity-coverage/node_modules/@solidity-parser/parser": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.19.0.tgz", + "integrity": "sha512-RV16k/qIxW/wWc+mLzV3ARyKUaMUTBy9tOLMzFhtNSKYeTAanQ3a5MudJKf/8arIFnA2L27SNjarQKmFg0w/jA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/solidity-coverage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/solidity-coverage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/solidity-coverage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/solidity-coverage/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/solidity-coverage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/solidity-coverage/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-coverage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true, + "license": "WTFPL OR MIT", + "peer": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/then-request/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-command-line-args": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", + "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "bin": { + "write-markdown": "dist/write-markdown.js" + } + }, + "node_modules/ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "typescript": ">=3.7.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true, + "license": "0BSD", + "peer": true + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typechain": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/prettier": "^2.1.1", + "debug": "^4.3.1", + "fs-extra": "^7.0.0", + "glob": "7.1.7", + "js-sha3": "^0.8.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "prettier": "^2.3.1", + "ts-command-line-args": "^2.2.0", + "ts-essentials": "^7.0.1" + }, + "bin": { + "typechain": "dist/cli/cli.js" + }, + "peerDependencies": { + "typescript": ">=4.3.0" + } + }, + "node_modules/typechain/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/typechain/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typechain/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/typechain/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typechain/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uglify-js": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", + "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici": { + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz", + "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz", + "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/web3-utils": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", + "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", + "dev": true, + "license": "LGPL-3.0", + "peer": true, + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/x/evm/embeds/package.json b/x/evm/embeds/package.json new file mode 100644 index 000000000..50174e8ff --- /dev/null +++ b/x/evm/embeds/package.json @@ -0,0 +1,21 @@ +{ + "name": "@nibiruchain/solidity", + "description": "Nibiru EVM solidity contracts and ABIs for Nibiru-specific precompiles and core protocol functionality", + "version": "0.0.2", + "private": false, + "files": [ + "/contracts/**/*.sol", + "/abi", + "!/contracts/**/Test*.sol" + ], + "keywords": ["solidity", "ethereum", "smart", "contracts", "evm", "nibiru", "blockchain"], + "license": "MIT", + "devDependencies": { + "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "bun": "^1.1.30", + "hardhat": "^2.22.5" + }, + "dependencies": { + "@openzeppelin/contracts": "^4.9.0" + } +} diff --git a/x/evm/errors.go b/x/evm/errors.go new file mode 100644 index 000000000..94ce6d3d8 --- /dev/null +++ b/x/evm/errors.go @@ -0,0 +1,70 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +const ( + codeErrInvalidState = uint32(iota) + 2 // NOTE: code 1 is reserved for internal errors + codeErrZeroAddress + codeErrInvalidAmount + codeErrInvalidGasPrice + codeErrInvalidGasFee + codeErrInvalidRefund + codeErrInvalidGasCap + codeErrInvalidBaseFee + codeErrInvalidAccount + codeErrInactivePrecompile +) + +var ( + // ErrInvalidState returns an error resulting from an invalid Storage State. + ErrInvalidState = errorsmod.Register(ModuleName, codeErrInvalidState, "invalid storage state") + + // ErrZeroAddress returns an error resulting from an zero (empty) ethereum Address. + ErrZeroAddress = errorsmod.Register(ModuleName, codeErrZeroAddress, "invalid zero address") + + // ErrInvalidAmount returns an error if a tx contains an invalid amount. + ErrInvalidAmount = errorsmod.Register(ModuleName, codeErrInvalidAmount, "invalid transaction amount") + + // ErrInvalidGasPrice returns an error if an invalid gas price is provided to the tx. + ErrInvalidGasPrice = errorsmod.Register(ModuleName, codeErrInvalidGasPrice, "invalid gas price") + + // ErrInvalidGasFee returns an error if the tx gas fee is out of bound. + ErrInvalidGasFee = errorsmod.Register(ModuleName, codeErrInvalidGasFee, "invalid gas fee") + + // ErrInvalidRefund returns an error if the gas refund value is invalid. + ErrInvalidRefund = errorsmod.Register(ModuleName, codeErrInvalidRefund, "invalid gas refund amount") + + // ErrInvalidGasCap returns an error if the gas cap value is negative or invalid + ErrInvalidGasCap = errorsmod.Register(ModuleName, codeErrInvalidGasCap, "invalid gas cap") + + // ErrInvalidBaseFee returns an error if the base fee cap value is invalid + ErrInvalidBaseFee = errorsmod.Register(ModuleName, codeErrInvalidBaseFee, "invalid base fee") + + // ErrInvalidAccount returns an error if the account is not an EVM compatible account + ErrInvalidAccount = errorsmod.Register(ModuleName, codeErrInvalidAccount, "account type is not a valid ethereum account") +) + +// NewRevertError unpacks the revert return bytes and returns a wrapped error +// with the return reason. +func NewRevertError(revertReason []byte) error { + reason, unpackingError := abi.UnpackRevert(revertReason) + + if unpackingError != nil { + return fmt.Errorf("execution reverted, but unable to parse reason \"%v\"", string(revertReason)) + } + + return fmt.Errorf("execution reverted with reason \"%v\"", reason) +} + +// RevertError is an API error that encompass an EVM revert with JSON error +// code and a binary data blob. +type RevertError struct { + error +} diff --git a/x/evm/events.go b/x/evm/events.go new file mode 100644 index 000000000..c31cae28e --- /dev/null +++ b/x/evm/events.go @@ -0,0 +1,103 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "fmt" + "strconv" + + "cosmossdk.io/errors" + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" +) + +// Evm module events +const ( + // proto.MessageName(new(evm.EventBlockBloom)) + TypeUrlEventBlockBloom = "eth.evm.v1.EventBlockBloom" + + // proto.MessageName(new(evm.EventTxLog)) + TypeUrlEventTxLog = "eth.evm.v1.EventTxLog" + + // proto.MessageName(new(evm.TypeUrlEventEthereumTx)) + TypeUrlEventEthereumTx = "eth.evm.v1.EventEthereumTx" + + // Untyped events and attribuges + + // Used in non-typed event "message" + MessageEventAttrTxType = "tx_type" + + // Used in non-typed event "pending_ethereum_tx" + PendingEthereumTxEvent = "pending_ethereum_tx" + PendingEthereumTxEventAttrEthHash = "eth_hash" + PendingEthereumTxEventAttrIndex = "index" +) + +func EventTxLogFromABCIEvent(event abci.Event) (*EventTxLog, error) { + typedProtoEvent, err := sdk.ParseTypedEvent(event) + if err != nil { + return nil, errors.Wrapf( + err, "failed to parse event of type %s", TypeUrlEventTxLog) + } + typedEvent, ok := (typedProtoEvent).(*EventTxLog) + if !ok { + return nil, errors.Wrapf( + err, "failed to parse event of type %s", TypeUrlEventTxLog) + } + return typedEvent, nil +} + +func EventBlockBloomFromABCIEvent(event abci.Event) (*EventBlockBloom, error) { + typedProtoEvent, err := sdk.ParseTypedEvent(event) + if err != nil { + return nil, errors.Wrapf( + err, "failed to parse event of type %s", TypeUrlEventBlockBloom) + } + typedEvent, ok := (typedProtoEvent).(*EventBlockBloom) + if !ok { + return nil, errors.Wrapf( + err, "failed to parse event of type %s", TypeUrlEventBlockBloom) + } + return typedEvent, nil +} + +func EventEthereumTxFromABCIEvent(event abci.Event) (*EventEthereumTx, error) { + typedProtoEvent, err := sdk.ParseTypedEvent(event) + if err != nil { + return nil, errors.Wrapf( + err, "failed to parse event of type %s", TypeUrlEventEthereumTx) + } + typedEvent, ok := (typedProtoEvent).(*EventEthereumTx) + if !ok { + return nil, errors.Wrapf( + err, "failed to parse event of type %s", TypeUrlEventEthereumTx) + } + return typedEvent, nil +} + +func GetEthHashAndIndexFromPendingEthereumTxEvent(event abci.Event) (gethcommon.Hash, int32, error) { + ethHash := gethcommon.Hash{} + txIndex := int32(-1) + + for _, attr := range event.Attributes { + if attr.Key == PendingEthereumTxEventAttrEthHash { + ethHash = gethcommon.HexToHash(attr.Value) + } + if attr.Key == PendingEthereumTxEventAttrIndex { + parsedIndex, err := strconv.ParseInt(attr.Value, 10, 32) + if err != nil { + return ethHash, -1, fmt.Errorf( + "failed to parse tx index from pending_ethereum_tx event, %s", attr.Value, + ) + } + txIndex = int32(parsedIndex) + } + } + if txIndex == -1 { + return ethHash, -1, fmt.Errorf("tx index not found in pending_ethereum_tx") + } + if ethHash.String() == "" { + return ethHash, -1, fmt.Errorf("eth hash not found in pending_ethereum_tx") + } + return ethHash, txIndex, nil +} diff --git a/x/evm/events.pb.go b/x/evm/events.pb.go new file mode 100644 index 000000000..e0efcbf02 --- /dev/null +++ b/x/evm/events.pb.go @@ -0,0 +1,2372 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: eth/evm/v1/events.proto + +package evm + +import ( + fmt "fmt" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// EventEthereumTx defines the event for an Ethereum transaction +type EventEthereumTx struct { + // amount + Amount string `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` + // eth_hash is the Ethereum hash of the transaction + EthHash string `protobuf:"bytes,2,opt,name=eth_hash,json=ethHash,proto3" json:"eth_hash,omitempty"` + // index of the transaction in the block + Index string `protobuf:"bytes,3,opt,name=index,proto3" json:"index,omitempty"` + // gas_used is the amount of gas used by the transaction + GasUsed string `protobuf:"bytes,4,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + // hash is the Tendermint hash of the transaction + Hash string `protobuf:"bytes,5,opt,name=hash,proto3" json:"hash,omitempty"` + // recipient of the transaction + Recipient string `protobuf:"bytes,6,opt,name=recipient,proto3" json:"recipient,omitempty"` + // vm_error contains a VM error should it occur + VmError string `protobuf:"bytes,7,opt,name=vm_error,json=vmError,proto3" json:"vm_error,omitempty"` +} + +func (m *EventEthereumTx) Reset() { *m = EventEthereumTx{} } +func (m *EventEthereumTx) String() string { return proto.CompactTextString(m) } +func (*EventEthereumTx) ProtoMessage() {} +func (*EventEthereumTx) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{0} +} +func (m *EventEthereumTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventEthereumTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventEthereumTx.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventEthereumTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventEthereumTx.Merge(m, src) +} +func (m *EventEthereumTx) XXX_Size() int { + return m.Size() +} +func (m *EventEthereumTx) XXX_DiscardUnknown() { + xxx_messageInfo_EventEthereumTx.DiscardUnknown(m) +} + +var xxx_messageInfo_EventEthereumTx proto.InternalMessageInfo + +func (m *EventEthereumTx) GetAmount() string { + if m != nil { + return m.Amount + } + return "" +} + +func (m *EventEthereumTx) GetEthHash() string { + if m != nil { + return m.EthHash + } + return "" +} + +func (m *EventEthereumTx) GetIndex() string { + if m != nil { + return m.Index + } + return "" +} + +func (m *EventEthereumTx) GetGasUsed() string { + if m != nil { + return m.GasUsed + } + return "" +} + +func (m *EventEthereumTx) GetHash() string { + if m != nil { + return m.Hash + } + return "" +} + +func (m *EventEthereumTx) GetRecipient() string { + if m != nil { + return m.Recipient + } + return "" +} + +func (m *EventEthereumTx) GetVmError() string { + if m != nil { + return m.VmError + } + return "" +} + +// EventTxLog defines the event for an Ethereum transaction log +type EventTxLog struct { + // tx_logs is an array of transaction logs + Logs []Log `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs"` +} + +func (m *EventTxLog) Reset() { *m = EventTxLog{} } +func (m *EventTxLog) String() string { return proto.CompactTextString(m) } +func (*EventTxLog) ProtoMessage() {} +func (*EventTxLog) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{1} +} +func (m *EventTxLog) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventTxLog) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventTxLog.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventTxLog) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventTxLog.Merge(m, src) +} +func (m *EventTxLog) XXX_Size() int { + return m.Size() +} +func (m *EventTxLog) XXX_DiscardUnknown() { + xxx_messageInfo_EventTxLog.DiscardUnknown(m) +} + +var xxx_messageInfo_EventTxLog proto.InternalMessageInfo + +func (m *EventTxLog) GetLogs() []Log { + if m != nil { + return m.Logs + } + return nil +} + +// EventBlockBloom defines an Ethereum block bloom filter event +type EventBlockBloom struct { + // bloom is the bloom filter of the block + Bloom string `protobuf:"bytes,1,opt,name=bloom,proto3" json:"bloom,omitempty"` +} + +func (m *EventBlockBloom) Reset() { *m = EventBlockBloom{} } +func (m *EventBlockBloom) String() string { return proto.CompactTextString(m) } +func (*EventBlockBloom) ProtoMessage() {} +func (*EventBlockBloom) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{2} +} +func (m *EventBlockBloom) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventBlockBloom) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventBlockBloom.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventBlockBloom) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventBlockBloom.Merge(m, src) +} +func (m *EventBlockBloom) XXX_Size() int { + return m.Size() +} +func (m *EventBlockBloom) XXX_DiscardUnknown() { + xxx_messageInfo_EventBlockBloom.DiscardUnknown(m) +} + +var xxx_messageInfo_EventBlockBloom proto.InternalMessageInfo + +func (m *EventBlockBloom) GetBloom() string { + if m != nil { + return m.Bloom + } + return "" +} + +// EventFunTokenCreated defines a fun token creation event. +type EventFunTokenCreated struct { + BankDenom string `protobuf:"bytes,1,opt,name=bank_denom,json=bankDenom,proto3" json:"bank_denom,omitempty"` + Erc20ContractAddress string `protobuf:"bytes,2,opt,name=erc20_contract_address,json=erc20ContractAddress,proto3" json:"erc20_contract_address,omitempty"` + Creator string `protobuf:"bytes,3,opt,name=creator,proto3" json:"creator,omitempty"` + IsMadeFromCoin bool `protobuf:"varint,4,opt,name=is_made_from_coin,json=isMadeFromCoin,proto3" json:"is_made_from_coin,omitempty"` +} + +func (m *EventFunTokenCreated) Reset() { *m = EventFunTokenCreated{} } +func (m *EventFunTokenCreated) String() string { return proto.CompactTextString(m) } +func (*EventFunTokenCreated) ProtoMessage() {} +func (*EventFunTokenCreated) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{3} +} +func (m *EventFunTokenCreated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventFunTokenCreated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventFunTokenCreated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventFunTokenCreated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventFunTokenCreated.Merge(m, src) +} +func (m *EventFunTokenCreated) XXX_Size() int { + return m.Size() +} +func (m *EventFunTokenCreated) XXX_DiscardUnknown() { + xxx_messageInfo_EventFunTokenCreated.DiscardUnknown(m) +} + +var xxx_messageInfo_EventFunTokenCreated proto.InternalMessageInfo + +func (m *EventFunTokenCreated) GetBankDenom() string { + if m != nil { + return m.BankDenom + } + return "" +} + +func (m *EventFunTokenCreated) GetErc20ContractAddress() string { + if m != nil { + return m.Erc20ContractAddress + } + return "" +} + +func (m *EventFunTokenCreated) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *EventFunTokenCreated) GetIsMadeFromCoin() bool { + if m != nil { + return m.IsMadeFromCoin + } + return false +} + +// ConvertCoinToEvm defines sending fun token to erc20 event. +type EventConvertCoinToEvm struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + Erc20ContractAddress string `protobuf:"bytes,2,opt,name=erc20_contract_address,json=erc20ContractAddress,proto3" json:"erc20_contract_address,omitempty"` + ToEthAddr string `protobuf:"bytes,3,opt,name=to_eth_addr,json=toEthAddr,proto3" json:"to_eth_addr,omitempty"` + BankCoin types.Coin `protobuf:"bytes,4,opt,name=bank_coin,json=bankCoin,proto3" json:"bank_coin" yaml:"bank_coin"` +} + +func (m *EventConvertCoinToEvm) Reset() { *m = EventConvertCoinToEvm{} } +func (m *EventConvertCoinToEvm) String() string { return proto.CompactTextString(m) } +func (*EventConvertCoinToEvm) ProtoMessage() {} +func (*EventConvertCoinToEvm) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{4} +} +func (m *EventConvertCoinToEvm) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventConvertCoinToEvm) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventConvertCoinToEvm.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventConvertCoinToEvm) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventConvertCoinToEvm.Merge(m, src) +} +func (m *EventConvertCoinToEvm) XXX_Size() int { + return m.Size() +} +func (m *EventConvertCoinToEvm) XXX_DiscardUnknown() { + xxx_messageInfo_EventConvertCoinToEvm.DiscardUnknown(m) +} + +var xxx_messageInfo_EventConvertCoinToEvm proto.InternalMessageInfo + +func (m *EventConvertCoinToEvm) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *EventConvertCoinToEvm) GetErc20ContractAddress() string { + if m != nil { + return m.Erc20ContractAddress + } + return "" +} + +func (m *EventConvertCoinToEvm) GetToEthAddr() string { + if m != nil { + return m.ToEthAddr + } + return "" +} + +func (m *EventConvertCoinToEvm) GetBankCoin() types.Coin { + if m != nil { + return m.BankCoin + } + return types.Coin{} +} + +// EventTransfer defines event for EVM transfer +type EventTransfer struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + Recipient string `protobuf:"bytes,2,opt,name=recipient,proto3" json:"recipient,omitempty"` + Amount string `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` +} + +func (m *EventTransfer) Reset() { *m = EventTransfer{} } +func (m *EventTransfer) String() string { return proto.CompactTextString(m) } +func (*EventTransfer) ProtoMessage() {} +func (*EventTransfer) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{5} +} +func (m *EventTransfer) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventTransfer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventTransfer.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventTransfer) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventTransfer.Merge(m, src) +} +func (m *EventTransfer) XXX_Size() int { + return m.Size() +} +func (m *EventTransfer) XXX_DiscardUnknown() { + xxx_messageInfo_EventTransfer.DiscardUnknown(m) +} + +var xxx_messageInfo_EventTransfer proto.InternalMessageInfo + +func (m *EventTransfer) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *EventTransfer) GetRecipient() string { + if m != nil { + return m.Recipient + } + return "" +} + +func (m *EventTransfer) GetAmount() string { + if m != nil { + return m.Amount + } + return "" +} + +// EventContractDeployed defines event for EVM contract deployment +type EventContractDeployed struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + ContractAddr string `protobuf:"bytes,2,opt,name=contract_addr,json=contractAddr,proto3" json:"contract_addr,omitempty"` +} + +func (m *EventContractDeployed) Reset() { *m = EventContractDeployed{} } +func (m *EventContractDeployed) String() string { return proto.CompactTextString(m) } +func (*EventContractDeployed) ProtoMessage() {} +func (*EventContractDeployed) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{6} +} +func (m *EventContractDeployed) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventContractDeployed) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventContractDeployed.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventContractDeployed) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventContractDeployed.Merge(m, src) +} +func (m *EventContractDeployed) XXX_Size() int { + return m.Size() +} +func (m *EventContractDeployed) XXX_DiscardUnknown() { + xxx_messageInfo_EventContractDeployed.DiscardUnknown(m) +} + +var xxx_messageInfo_EventContractDeployed proto.InternalMessageInfo + +func (m *EventContractDeployed) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *EventContractDeployed) GetContractAddr() string { + if m != nil { + return m.ContractAddr + } + return "" +} + +// EventContractExecuted defines event for EVM contract execution +type EventContractExecuted struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + ContractAddr string `protobuf:"bytes,2,opt,name=contract_addr,json=contractAddr,proto3" json:"contract_addr,omitempty"` +} + +func (m *EventContractExecuted) Reset() { *m = EventContractExecuted{} } +func (m *EventContractExecuted) String() string { return proto.CompactTextString(m) } +func (*EventContractExecuted) ProtoMessage() {} +func (*EventContractExecuted) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{7} +} +func (m *EventContractExecuted) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventContractExecuted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventContractExecuted.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventContractExecuted) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventContractExecuted.Merge(m, src) +} +func (m *EventContractExecuted) XXX_Size() int { + return m.Size() +} +func (m *EventContractExecuted) XXX_DiscardUnknown() { + xxx_messageInfo_EventContractExecuted.DiscardUnknown(m) +} + +var xxx_messageInfo_EventContractExecuted proto.InternalMessageInfo + +func (m *EventContractExecuted) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *EventContractExecuted) GetContractAddr() string { + if m != nil { + return m.ContractAddr + } + return "" +} + +func init() { + proto.RegisterType((*EventEthereumTx)(nil), "eth.evm.v1.EventEthereumTx") + proto.RegisterType((*EventTxLog)(nil), "eth.evm.v1.EventTxLog") + proto.RegisterType((*EventBlockBloom)(nil), "eth.evm.v1.EventBlockBloom") + proto.RegisterType((*EventFunTokenCreated)(nil), "eth.evm.v1.EventFunTokenCreated") + proto.RegisterType((*EventConvertCoinToEvm)(nil), "eth.evm.v1.EventConvertCoinToEvm") + proto.RegisterType((*EventTransfer)(nil), "eth.evm.v1.EventTransfer") + proto.RegisterType((*EventContractDeployed)(nil), "eth.evm.v1.EventContractDeployed") + proto.RegisterType((*EventContractExecuted)(nil), "eth.evm.v1.EventContractExecuted") +} + +func init() { proto.RegisterFile("eth/evm/v1/events.proto", fileDescriptor_f8bc26b53c788f17) } + +var fileDescriptor_f8bc26b53c788f17 = []byte{ + // 628 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xcf, 0x6e, 0xd3, 0x4e, + 0x10, 0x8e, 0xdb, 0xf4, 0x4f, 0xb6, 0xbf, 0xfe, 0x0a, 0x56, 0x28, 0x6e, 0x05, 0x6e, 0x65, 0x24, + 0x68, 0x2f, 0x36, 0x09, 0x48, 0x48, 0x9c, 0x20, 0x69, 0x2a, 0x0e, 0x05, 0xa1, 0x28, 0x5c, 0x90, + 0x90, 0xb5, 0xb6, 0xa7, 0xb6, 0xd5, 0xec, 0x4e, 0xb5, 0xbb, 0xb6, 0xd2, 0xb7, 0xe0, 0x51, 0x78, + 0x06, 0x4e, 0x3d, 0xf6, 0x06, 0xa7, 0x0a, 0xb5, 0x6f, 0xc0, 0x13, 0xa0, 0x5d, 0xbb, 0x4d, 0x03, + 0xea, 0x05, 0x6e, 0x33, 0xdf, 0xfc, 0xd9, 0x99, 0x6f, 0x3e, 0x2d, 0xb9, 0x0f, 0x2a, 0x0b, 0xa0, + 0x64, 0x41, 0xd9, 0x09, 0xa0, 0x04, 0xae, 0xa4, 0x7f, 0x2c, 0x50, 0xa1, 0x4d, 0x40, 0x65, 0x3e, + 0x94, 0xcc, 0x2f, 0x3b, 0x9b, 0x6e, 0x8c, 0x92, 0xa1, 0x0c, 0x22, 0x2a, 0x21, 0x28, 0x3b, 0x11, + 0x28, 0xda, 0x09, 0x62, 0xcc, 0x79, 0x95, 0xbb, 0xd9, 0x4e, 0x31, 0x45, 0x63, 0x06, 0xda, 0xba, + 0x42, 0x67, 0x5a, 0xb3, 0x0a, 0xf5, 0xbe, 0x5a, 0x64, 0x6d, 0xa0, 0x1f, 0x1a, 0xa8, 0x0c, 0x04, + 0x14, 0x6c, 0x34, 0xb1, 0xd7, 0xc9, 0x22, 0x65, 0x58, 0x70, 0xe5, 0x58, 0xdb, 0xd6, 0x4e, 0x6b, + 0x58, 0x7b, 0xf6, 0x06, 0x59, 0x06, 0x95, 0x85, 0x19, 0x95, 0x99, 0x33, 0x67, 0x22, 0x4b, 0xa0, + 0xb2, 0x37, 0x54, 0x66, 0x76, 0x9b, 0x2c, 0xe4, 0x3c, 0x81, 0x89, 0x33, 0x6f, 0xf0, 0xca, 0xd1, + 0x05, 0x29, 0x95, 0x61, 0x21, 0x21, 0x71, 0x9a, 0x55, 0x41, 0x4a, 0xe5, 0x07, 0x09, 0x89, 0x6d, + 0x93, 0xa6, 0xe9, 0xb3, 0x60, 0x60, 0x63, 0xdb, 0x0f, 0x48, 0x4b, 0x40, 0x9c, 0x1f, 0xe7, 0xc0, + 0x95, 0xb3, 0x68, 0x02, 0x53, 0x40, 0x37, 0x2b, 0x59, 0x08, 0x42, 0xa0, 0x70, 0x96, 0xaa, 0x66, + 0x25, 0x1b, 0x68, 0xd7, 0x7b, 0x41, 0x88, 0xd9, 0x61, 0x34, 0x39, 0xc0, 0xd4, 0xde, 0x25, 0xcd, + 0x31, 0xa6, 0xd2, 0xb1, 0xb6, 0xe7, 0x77, 0x56, 0xba, 0x6b, 0xfe, 0x94, 0x39, 0xff, 0x00, 0xd3, + 0x5e, 0xf3, 0xf4, 0x7c, 0xab, 0x31, 0x34, 0x29, 0xde, 0x93, 0x7a, 0xf9, 0xde, 0x18, 0xe3, 0xa3, + 0xde, 0x18, 0x91, 0xe9, 0x4d, 0x22, 0x6d, 0xd4, 0xbb, 0x57, 0x8e, 0xf7, 0xc5, 0x22, 0x6d, 0x93, + 0xb9, 0x5f, 0xf0, 0x11, 0x1e, 0x01, 0xef, 0x0b, 0xa0, 0x0a, 0x12, 0xfb, 0x21, 0x21, 0x11, 0xe5, + 0x47, 0x61, 0x02, 0xfc, 0xba, 0xa6, 0xa5, 0x91, 0x3d, 0x0d, 0xd8, 0xcf, 0xc9, 0x3a, 0x88, 0xb8, + 0xfb, 0x34, 0x8c, 0x91, 0x2b, 0x41, 0x63, 0x15, 0xd2, 0x24, 0x11, 0x20, 0x65, 0x4d, 0x60, 0xdb, + 0x44, 0xfb, 0x75, 0xf0, 0x75, 0x15, 0xb3, 0x1d, 0xb2, 0x14, 0xeb, 0xfe, 0x28, 0x6a, 0x3e, 0xaf, + 0x5c, 0x7b, 0x97, 0xdc, 0xcd, 0x65, 0xc8, 0x68, 0x02, 0xe1, 0xa1, 0x40, 0x16, 0xea, 0xab, 0x1b, + 0x6a, 0x97, 0x87, 0xff, 0xe7, 0xf2, 0x2d, 0x4d, 0x60, 0x5f, 0x20, 0xeb, 0x63, 0xce, 0xbd, 0x6f, + 0x16, 0xb9, 0x67, 0x46, 0xee, 0x23, 0x2f, 0x41, 0x28, 0x0d, 0x8e, 0x70, 0x50, 0x32, 0x7d, 0x5f, + 0x09, 0x3c, 0x01, 0x71, 0x75, 0xdf, 0xca, 0xfb, 0xcb, 0x61, 0x5d, 0xb2, 0xa2, 0x30, 0xd4, 0xc2, + 0xd0, 0xd9, 0xf5, 0xc0, 0x2d, 0x85, 0x03, 0x95, 0xe9, 0x14, 0xfb, 0x3d, 0x31, 0x7c, 0x4c, 0x47, + 0x5d, 0xe9, 0x6e, 0xf8, 0x95, 0x82, 0x7d, 0xad, 0x60, 0xbf, 0x56, 0xb0, 0xaf, 0x07, 0xec, 0x39, + 0xfa, 0x3a, 0x3f, 0xcf, 0xb7, 0xee, 0x9c, 0x50, 0x36, 0x7e, 0xe9, 0x5d, 0x57, 0x7a, 0xc3, 0x65, + 0x6d, 0x9b, 0xcd, 0x3e, 0x91, 0xd5, 0xea, 0xdc, 0x82, 0x72, 0x79, 0x08, 0xe2, 0xd6, 0x85, 0x66, + 0x04, 0x35, 0xf7, 0xbb, 0xa0, 0xa6, 0x32, 0x9f, 0xbf, 0x29, 0x73, 0x6f, 0x34, 0xe5, 0xcd, 0x2c, + 0xba, 0x07, 0xc7, 0x63, 0x3c, 0x81, 0xe4, 0xd6, 0x67, 0x1e, 0x91, 0xd5, 0x19, 0xc6, 0xea, 0xa7, + 0xfe, 0x8b, 0x6f, 0x30, 0xf5, 0x47, 0xd7, 0xc1, 0x04, 0xe2, 0x42, 0xfd, 0x63, 0xd7, 0xde, 0xab, + 0xd3, 0x0b, 0xd7, 0x3a, 0xbb, 0x70, 0xad, 0x1f, 0x17, 0xae, 0xf5, 0xf9, 0xd2, 0x6d, 0x9c, 0x5d, + 0xba, 0x8d, 0xef, 0x97, 0x6e, 0xe3, 0xe3, 0xe3, 0x34, 0x57, 0x59, 0x11, 0xf9, 0x31, 0xb2, 0xe0, + 0x5d, 0x1e, 0xe5, 0xa2, 0xe8, 0x67, 0x34, 0xe7, 0x01, 0x37, 0x76, 0x50, 0x76, 0x83, 0x89, 0xfe, + 0x06, 0xa2, 0x45, 0xf3, 0x0f, 0x3c, 0xfb, 0x15, 0x00, 0x00, 0xff, 0xff, 0x52, 0xaf, 0x8b, 0xfe, + 0x7a, 0x04, 0x00, 0x00, +} + +func (m *EventEthereumTx) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventEthereumTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventEthereumTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.VmError) > 0 { + i -= len(m.VmError) + copy(dAtA[i:], m.VmError) + i = encodeVarintEvents(dAtA, i, uint64(len(m.VmError))) + i-- + dAtA[i] = 0x3a + } + if len(m.Recipient) > 0 { + i -= len(m.Recipient) + copy(dAtA[i:], m.Recipient) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Recipient))) + i-- + dAtA[i] = 0x32 + } + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0x2a + } + if len(m.GasUsed) > 0 { + i -= len(m.GasUsed) + copy(dAtA[i:], m.GasUsed) + i = encodeVarintEvents(dAtA, i, uint64(len(m.GasUsed))) + i-- + dAtA[i] = 0x22 + } + if len(m.Index) > 0 { + i -= len(m.Index) + copy(dAtA[i:], m.Index) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Index))) + i-- + dAtA[i] = 0x1a + } + if len(m.EthHash) > 0 { + i -= len(m.EthHash) + copy(dAtA[i:], m.EthHash) + i = encodeVarintEvents(dAtA, i, uint64(len(m.EthHash))) + i-- + dAtA[i] = 0x12 + } + if len(m.Amount) > 0 { + i -= len(m.Amount) + copy(dAtA[i:], m.Amount) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Amount))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventTxLog) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventTxLog) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventTxLog) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Logs) > 0 { + for iNdEx := len(m.Logs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Logs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *EventBlockBloom) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventBlockBloom) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventBlockBloom) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Bloom) > 0 { + i -= len(m.Bloom) + copy(dAtA[i:], m.Bloom) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Bloom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventFunTokenCreated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventFunTokenCreated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventFunTokenCreated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsMadeFromCoin { + i-- + if m.IsMadeFromCoin { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0x1a + } + if len(m.Erc20ContractAddress) > 0 { + i -= len(m.Erc20ContractAddress) + copy(dAtA[i:], m.Erc20ContractAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Erc20ContractAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.BankDenom) > 0 { + i -= len(m.BankDenom) + copy(dAtA[i:], m.BankDenom) + i = encodeVarintEvents(dAtA, i, uint64(len(m.BankDenom))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventConvertCoinToEvm) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventConvertCoinToEvm) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventConvertCoinToEvm) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.BankCoin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ToEthAddr) > 0 { + i -= len(m.ToEthAddr) + copy(dAtA[i:], m.ToEthAddr) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ToEthAddr))) + i-- + dAtA[i] = 0x1a + } + if len(m.Erc20ContractAddress) > 0 { + i -= len(m.Erc20ContractAddress) + copy(dAtA[i:], m.Erc20ContractAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Erc20ContractAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventTransfer) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventTransfer) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventTransfer) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + i -= len(m.Amount) + copy(dAtA[i:], m.Amount) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Amount))) + i-- + dAtA[i] = 0x1a + } + if len(m.Recipient) > 0 { + i -= len(m.Recipient) + copy(dAtA[i:], m.Recipient) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Recipient))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventContractDeployed) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventContractDeployed) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventContractDeployed) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ContractAddr) > 0 { + i -= len(m.ContractAddr) + copy(dAtA[i:], m.ContractAddr) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ContractAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventContractExecuted) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventContractExecuted) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventContractExecuted) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ContractAddr) > 0 { + i -= len(m.ContractAddr) + copy(dAtA[i:], m.ContractAddr) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ContractAddr))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { + offset -= sovEvents(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *EventEthereumTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Amount) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.EthHash) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Index) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.GasUsed) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Recipient) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.VmError) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventTxLog) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Logs) > 0 { + for _, e := range m.Logs { + l = e.Size() + n += 1 + l + sovEvents(uint64(l)) + } + } + return n +} + +func (m *EventBlockBloom) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Bloom) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventFunTokenCreated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.BankDenom) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Erc20ContractAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.IsMadeFromCoin { + n += 2 + } + return n +} + +func (m *EventConvertCoinToEvm) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Erc20ContractAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ToEthAddr) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = m.BankCoin.Size() + n += 1 + l + sovEvents(uint64(l)) + return n +} + +func (m *EventTransfer) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Recipient) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Amount) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventContractDeployed) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ContractAddr) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventContractExecuted) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ContractAddr) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func sovEvents(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvents(x uint64) (n int) { + return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *EventEthereumTx) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventEthereumTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventEthereumTx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EthHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EthHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Index = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GasUsed = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Recipient", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Recipient = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VmError", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VmError = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventTxLog) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventTxLog: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventTxLog: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Logs = append(m.Logs, Log{}) + if err := m.Logs[len(m.Logs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventBlockBloom) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventBlockBloom: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventBlockBloom: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bloom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bloom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventFunTokenCreated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventFunTokenCreated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventFunTokenCreated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BankDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BankDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Erc20ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Erc20ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsMadeFromCoin", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsMadeFromCoin = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventConvertCoinToEvm) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventConvertCoinToEvm: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventConvertCoinToEvm: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Erc20ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Erc20ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToEthAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToEthAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BankCoin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BankCoin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventTransfer) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventTransfer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventTransfer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Recipient", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Recipient = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventContractDeployed) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventContractDeployed: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventContractDeployed: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventContractExecuted) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventContractExecuted: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventContractExecuted: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContractAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContractAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvents(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthEvents + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvents + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvents + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/evm/evm.go b/x/evm/evm.go new file mode 100644 index 000000000..d0e368aad --- /dev/null +++ b/x/evm/evm.go @@ -0,0 +1,50 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "fmt" + + "github.com/cometbft/cometbft/crypto/tmhash" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// FIXME: Explore problems arrising from ERC1155 creating multiple fungible +// tokens that are valid ERC20s with the same address. +// https://github.com/NibiruChain/nibiru/issues/1933 +func (fun FunToken) ID() []byte { + return NewFunTokenID(fun.Erc20Addr.Address, fun.BankDenom) +} + +func NewFunTokenID(erc20 gethcommon.Address, bankDenom string) []byte { + return tmhash.Sum([]byte(erc20.String() + "|" + bankDenom)) +} + +func funTokenValidationError(err error) error { + return fmt.Errorf("FunTokenError: %s", err.Error()) +} + +func (fun FunToken) Validate() error { + if err := sdk.ValidateDenom(fun.BankDenom); err != nil { + return funTokenValidationError(err) + } + + return nil +} + +// NewFunToken is a canonical constructor for the [FunToken] type. Using this +// function helps guarantee a consistent string representation from the +// hex-encoded Ethereum address. +func NewFunToken( + erc20 gethcommon.Address, bankDenom string, isMadeFromCoin bool, +) FunToken { + return FunToken{ + Erc20Addr: eth.EIP55Addr{ + Address: erc20, + }, + BankDenom: bankDenom, + IsMadeFromCoin: isMadeFromCoin, + } +} diff --git a/x/evm/evm.pb.go b/x/evm/evm.pb.go new file mode 100644 index 000000000..fac1e30bb --- /dev/null +++ b/x/evm/evm.pb.go @@ -0,0 +1,2551 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: eth/evm/v1/evm.proto + +package evm + +import ( + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + github_com_NibiruChain_nibiru_v2_eth "github.com/NibiruChain/nibiru/v2/eth" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// FunToken is a fungible token mapping between a Bank Coin and a corresponding +// ERC-20 smart contract. Bank Coins here refer to tokens like NIBI, IBC +// coins (ICS-20), and token factory coins, which are each represented by the +// "Coin" type in Golang. +type FunToken struct { + // Hexadecimal address of the ERC20 token to which the `FunToken` maps + Erc20Addr github_com_NibiruChain_nibiru_v2_eth.EIP55Addr `protobuf:"bytes,1,opt,name=erc20_addr,json=erc20Addr,proto3,customtype=github.com/NibiruChain/nibiru/v2/eth.EIP55Addr" json:"erc20_addr"` + // bank_denom: Coin denomination in the Bank Module. + BankDenom string `protobuf:"bytes,2,opt,name=bank_denom,json=bankDenom,proto3" json:"bank_denom,omitempty"` + // True if the `FunToken` mapping was created from an existing Bank Coin and + // the ERC-20 contract gets deployed by the module account. False if the + // mapping was created from an externally owned ERC-20 contract. + IsMadeFromCoin bool `protobuf:"varint,3,opt,name=is_made_from_coin,json=isMadeFromCoin,proto3" json:"is_made_from_coin,omitempty"` +} + +func (m *FunToken) Reset() { *m = FunToken{} } +func (m *FunToken) String() string { return proto.CompactTextString(m) } +func (*FunToken) ProtoMessage() {} +func (*FunToken) Descriptor() ([]byte, []int) { + return fileDescriptor_98abbdadb327b7d0, []int{0} +} +func (m *FunToken) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FunToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FunToken.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *FunToken) XXX_Merge(src proto.Message) { + xxx_messageInfo_FunToken.Merge(m, src) +} +func (m *FunToken) XXX_Size() int { + return m.Size() +} +func (m *FunToken) XXX_DiscardUnknown() { + xxx_messageInfo_FunToken.DiscardUnknown(m) +} + +var xxx_messageInfo_FunToken proto.InternalMessageInfo + +func (m *FunToken) GetBankDenom() string { + if m != nil { + return m.BankDenom + } + return "" +} + +func (m *FunToken) GetIsMadeFromCoin() bool { + if m != nil { + return m.IsMadeFromCoin + } + return false +} + +// Params defines the EVM module parameters +type Params struct { + // extra_eips defines the additional EIPs for the vm.Config + ExtraEIPs []int64 `protobuf:"varint,4,rep,packed,name=extra_eips,json=extraEips,proto3" json:"extra_eips,omitempty" yaml:"extra_eips"` + // evm_channels is the list of channel identifiers from EVM compatible chains + EVMChannels []string `protobuf:"bytes,8,rep,name=evm_channels,json=evmChannels,proto3" json:"evm_channels,omitempty"` + // Fee deducted and burned when calling "CreateFunToken" in units of + // "evm_denom". + CreateFuntokenFee cosmossdk_io_math.Int `protobuf:"bytes,9,opt,name=create_funtoken_fee,json=createFuntokenFee,proto3,customtype=cosmossdk.io/math.Int" json:"create_funtoken_fee"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_98abbdadb327b7d0, []int{1} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetExtraEIPs() []int64 { + if m != nil { + return m.ExtraEIPs + } + return nil +} + +func (m *Params) GetEVMChannels() []string { + if m != nil { + return m.EVMChannels + } + return nil +} + +// State represents a single Storage key value pair item. +type State struct { + // key is the stored key + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // value is the stored value for the given key + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *State) Reset() { *m = State{} } +func (m *State) String() string { return proto.CompactTextString(m) } +func (*State) ProtoMessage() {} +func (*State) Descriptor() ([]byte, []int) { + return fileDescriptor_98abbdadb327b7d0, []int{2} +} +func (m *State) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *State) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_State.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *State) XXX_Merge(src proto.Message) { + xxx_messageInfo_State.Merge(m, src) +} +func (m *State) XXX_Size() int { + return m.Size() +} +func (m *State) XXX_DiscardUnknown() { + xxx_messageInfo_State.DiscardUnknown(m) +} + +var xxx_messageInfo_State proto.InternalMessageInfo + +func (m *State) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +func (m *State) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +// Log represents an protobuf compatible Ethereum Log that defines a contract +// log event. These events are generated by the LOG opcode and stored/indexed by +// the node. +// +// NOTE: address, topics and data are consensus fields. The rest of the fields +// are derived, i.e. filled in by the nodes, but not secured by consensus. +type Log struct { + // address of the contract that generated the event + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // topics is a list of topics provided by the contract. + Topics []string `protobuf:"bytes,2,rep,name=topics,proto3" json:"topics,omitempty"` + // data which is supplied by the contract, usually ABI-encoded + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + // block_number of the block in which the transaction was included + BlockNumber uint64 `protobuf:"varint,4,opt,name=block_number,json=blockNumber,proto3" json:"blockNumber"` + // tx_hash is the transaction hash + TxHash string `protobuf:"bytes,5,opt,name=tx_hash,json=txHash,proto3" json:"transactionHash"` + // tx_index of the transaction in the block + TxIndex uint64 `protobuf:"varint,6,opt,name=tx_index,json=txIndex,proto3" json:"transactionIndex"` + // block_hash of the block in which the transaction was included + BlockHash string `protobuf:"bytes,7,opt,name=block_hash,json=blockHash,proto3" json:"blockHash"` + // index of the log in the block + Index uint64 `protobuf:"varint,8,opt,name=index,proto3" json:"logIndex"` + // removed is true if this log was reverted due to a chain + // reorganisation. You must pay attention to this field if you receive logs + // through a filter query. + Removed bool `protobuf:"varint,9,opt,name=removed,proto3" json:"removed,omitempty"` +} + +func (m *Log) Reset() { *m = Log{} } +func (m *Log) String() string { return proto.CompactTextString(m) } +func (*Log) ProtoMessage() {} +func (*Log) Descriptor() ([]byte, []int) { + return fileDescriptor_98abbdadb327b7d0, []int{3} +} +func (m *Log) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Log) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Log.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Log) XXX_Merge(src proto.Message) { + xxx_messageInfo_Log.Merge(m, src) +} +func (m *Log) XXX_Size() int { + return m.Size() +} +func (m *Log) XXX_DiscardUnknown() { + xxx_messageInfo_Log.DiscardUnknown(m) +} + +var xxx_messageInfo_Log proto.InternalMessageInfo + +func (m *Log) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *Log) GetTopics() []string { + if m != nil { + return m.Topics + } + return nil +} + +func (m *Log) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *Log) GetBlockNumber() uint64 { + if m != nil { + return m.BlockNumber + } + return 0 +} + +func (m *Log) GetTxHash() string { + if m != nil { + return m.TxHash + } + return "" +} + +func (m *Log) GetTxIndex() uint64 { + if m != nil { + return m.TxIndex + } + return 0 +} + +func (m *Log) GetBlockHash() string { + if m != nil { + return m.BlockHash + } + return "" +} + +func (m *Log) GetIndex() uint64 { + if m != nil { + return m.Index + } + return 0 +} + +func (m *Log) GetRemoved() bool { + if m != nil { + return m.Removed + } + return false +} + +// AccessTuple is the element type of an access list. +type AccessTuple struct { + // address is a hex formatted ethereum address + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // storage_keys are hex formatted hashes of the storage keys + StorageKeys []string `protobuf:"bytes,2,rep,name=storage_keys,json=storageKeys,proto3" json:"storageKeys"` +} + +func (m *AccessTuple) Reset() { *m = AccessTuple{} } +func (m *AccessTuple) String() string { return proto.CompactTextString(m) } +func (*AccessTuple) ProtoMessage() {} +func (*AccessTuple) Descriptor() ([]byte, []int) { + return fileDescriptor_98abbdadb327b7d0, []int{4} +} +func (m *AccessTuple) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AccessTuple) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AccessTuple.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AccessTuple) XXX_Merge(src proto.Message) { + xxx_messageInfo_AccessTuple.Merge(m, src) +} +func (m *AccessTuple) XXX_Size() int { + return m.Size() +} +func (m *AccessTuple) XXX_DiscardUnknown() { + xxx_messageInfo_AccessTuple.DiscardUnknown(m) +} + +var xxx_messageInfo_AccessTuple proto.InternalMessageInfo + +// TracerConfig stores additional tracer args. For geth it's only one attr: +// onlyTopCall +type TracerConfig struct { + OnlyTopCall bool `protobuf:"varint,1,opt,name=only_top_call,json=onlyTopCall,proto3" json:"onlyTopCall"` +} + +func (m *TracerConfig) Reset() { *m = TracerConfig{} } +func (m *TracerConfig) String() string { return proto.CompactTextString(m) } +func (*TracerConfig) ProtoMessage() {} +func (*TracerConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_98abbdadb327b7d0, []int{5} +} +func (m *TracerConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TracerConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TracerConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TracerConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_TracerConfig.Merge(m, src) +} +func (m *TracerConfig) XXX_Size() int { + return m.Size() +} +func (m *TracerConfig) XXX_DiscardUnknown() { + xxx_messageInfo_TracerConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_TracerConfig proto.InternalMessageInfo + +func (m *TracerConfig) GetOnlyTopCall() bool { + if m != nil { + return m.OnlyTopCall + } + return false +} + +// TraceConfig holds extra parameters to trace functions. +type TraceConfig struct { + // tracer is a custom javascript tracer + Tracer string `protobuf:"bytes,1,opt,name=tracer,proto3" json:"tracer,omitempty"` + // timeout overrides the default timeout of 5 seconds for JavaScript-based + // tracing calls + Timeout string `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"` + // reexec defines the number of blocks the tracer is willing to go back + Reexec uint64 `protobuf:"varint,3,opt,name=reexec,proto3" json:"reexec,omitempty"` + // disable_stack switches stack capture + DisableStack bool `protobuf:"varint,5,opt,name=disable_stack,json=disableStack,proto3" json:"disableStack"` + // disable_storage switches storage capture + DisableStorage bool `protobuf:"varint,6,opt,name=disable_storage,json=disableStorage,proto3" json:"disableStorage"` + // debug can be used to print output during capture end + Debug bool `protobuf:"varint,8,opt,name=debug,proto3" json:"debug,omitempty"` + // limit defines the maximum length of output, but zero means unlimited + Limit int32 `protobuf:"varint,9,opt,name=limit,proto3" json:"limit,omitempty"` + // enable_memory switches memory capture + EnableMemory bool `protobuf:"varint,11,opt,name=enable_memory,json=enableMemory,proto3" json:"enableMemory"` + // enable_return_data switches the capture of return data + EnableReturnData bool `protobuf:"varint,12,opt,name=enable_return_data,json=enableReturnData,proto3" json:"enableReturnData"` + // tracer_config configures the tracer options + TracerConfig *TracerConfig `protobuf:"bytes,13,opt,name=tracer_config,json=tracerConfig,proto3" json:"tracerConfig"` +} + +func (m *TraceConfig) Reset() { *m = TraceConfig{} } +func (m *TraceConfig) String() string { return proto.CompactTextString(m) } +func (*TraceConfig) ProtoMessage() {} +func (*TraceConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_98abbdadb327b7d0, []int{6} +} +func (m *TraceConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TraceConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TraceConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TraceConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_TraceConfig.Merge(m, src) +} +func (m *TraceConfig) XXX_Size() int { + return m.Size() +} +func (m *TraceConfig) XXX_DiscardUnknown() { + xxx_messageInfo_TraceConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_TraceConfig proto.InternalMessageInfo + +func (m *TraceConfig) GetTracer() string { + if m != nil { + return m.Tracer + } + return "" +} + +func (m *TraceConfig) GetTimeout() string { + if m != nil { + return m.Timeout + } + return "" +} + +func (m *TraceConfig) GetReexec() uint64 { + if m != nil { + return m.Reexec + } + return 0 +} + +func (m *TraceConfig) GetDisableStack() bool { + if m != nil { + return m.DisableStack + } + return false +} + +func (m *TraceConfig) GetDisableStorage() bool { + if m != nil { + return m.DisableStorage + } + return false +} + +func (m *TraceConfig) GetDebug() bool { + if m != nil { + return m.Debug + } + return false +} + +func (m *TraceConfig) GetLimit() int32 { + if m != nil { + return m.Limit + } + return 0 +} + +func (m *TraceConfig) GetEnableMemory() bool { + if m != nil { + return m.EnableMemory + } + return false +} + +func (m *TraceConfig) GetEnableReturnData() bool { + if m != nil { + return m.EnableReturnData + } + return false +} + +func (m *TraceConfig) GetTracerConfig() *TracerConfig { + if m != nil { + return m.TracerConfig + } + return nil +} + +func init() { + proto.RegisterType((*FunToken)(nil), "eth.evm.v1.FunToken") + proto.RegisterType((*Params)(nil), "eth.evm.v1.Params") + proto.RegisterType((*State)(nil), "eth.evm.v1.State") + proto.RegisterType((*Log)(nil), "eth.evm.v1.Log") + proto.RegisterType((*AccessTuple)(nil), "eth.evm.v1.AccessTuple") + proto.RegisterType((*TracerConfig)(nil), "eth.evm.v1.TracerConfig") + proto.RegisterType((*TraceConfig)(nil), "eth.evm.v1.TraceConfig") +} + +func init() { proto.RegisterFile("eth/evm/v1/evm.proto", fileDescriptor_98abbdadb327b7d0) } + +var fileDescriptor_98abbdadb327b7d0 = []byte{ + // 954 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x55, 0x41, 0x6f, 0xe3, 0x44, + 0x14, 0xae, 0x1b, 0x27, 0x71, 0x26, 0xe9, 0xd6, 0x9d, 0x16, 0x64, 0x21, 0x6d, 0x1c, 0xf9, 0x80, + 0x82, 0xb4, 0x4a, 0xd8, 0xae, 0xca, 0xa1, 0x5c, 0x68, 0xb2, 0xad, 0x68, 0xa0, 0x4b, 0x35, 0x5b, + 0x38, 0x70, 0xb1, 0x26, 0xf6, 0x6b, 0x62, 0xc5, 0xf6, 0x44, 0x33, 0xe3, 0x28, 0xf9, 0x07, 0x1c, + 0xf9, 0x09, 0x7b, 0xe7, 0x8f, 0xac, 0x38, 0xed, 0x11, 0x71, 0xb0, 0x50, 0x7b, 0x41, 0x39, 0x72, + 0x42, 0x9c, 0xd0, 0x8c, 0xdd, 0x4d, 0x0a, 0x12, 0x9c, 0xfc, 0xbe, 0xef, 0xcd, 0x7b, 0xf3, 0xe6, + 0xfb, 0xc6, 0x36, 0x3a, 0x02, 0x39, 0xed, 0xc3, 0x22, 0xe9, 0x2f, 0x9e, 0xab, 0x47, 0x6f, 0xce, + 0x99, 0x64, 0x18, 0x81, 0x9c, 0xf6, 0x14, 0x5c, 0x3c, 0xff, 0xe8, 0x68, 0xc2, 0x26, 0x4c, 0xd3, + 0x7d, 0x15, 0x15, 0x2b, 0xbc, 0x9f, 0x0c, 0x64, 0x5d, 0x64, 0xe9, 0x0d, 0x9b, 0x41, 0x8a, 0xbf, + 0x45, 0x08, 0x78, 0x70, 0xfc, 0xa9, 0x4f, 0xc3, 0x90, 0x3b, 0x46, 0xc7, 0xe8, 0x36, 0x06, 0x9f, + 0xbd, 0xcd, 0xdd, 0x9d, 0x5f, 0x73, 0xb7, 0x37, 0x89, 0xe4, 0x34, 0x1b, 0xf7, 0x02, 0x96, 0xf4, + 0x5f, 0x45, 0xe3, 0x88, 0x67, 0xc3, 0x29, 0x8d, 0xd2, 0x7e, 0xaa, 0xe3, 0xfe, 0xe2, 0xb8, 0xaf, + 0xf6, 0x3a, 0xbf, 0xbc, 0x3e, 0x39, 0x39, 0x0b, 0x43, 0x4e, 0x1a, 0xba, 0x93, 0x0a, 0xf1, 0x53, + 0x84, 0xc6, 0x34, 0x9d, 0xf9, 0x21, 0xa4, 0x2c, 0x71, 0x76, 0x55, 0x5b, 0xd2, 0x50, 0xcc, 0x4b, + 0x45, 0xe0, 0x4f, 0xd0, 0x41, 0x24, 0xfc, 0x84, 0x86, 0xe0, 0xdf, 0x72, 0x96, 0xf8, 0x01, 0x8b, + 0x52, 0xa7, 0xd2, 0x31, 0xba, 0x16, 0x79, 0x12, 0x89, 0x2b, 0x1a, 0xc2, 0x05, 0x67, 0xc9, 0x90, + 0x45, 0xa9, 0xf7, 0xa7, 0x81, 0x6a, 0xd7, 0x94, 0xd3, 0x44, 0xe0, 0x33, 0x84, 0x60, 0x29, 0x39, + 0xf5, 0x21, 0x9a, 0x0b, 0xc7, 0xec, 0x54, 0xba, 0x95, 0x81, 0x77, 0x97, 0xbb, 0x8d, 0x73, 0xc5, + 0x9e, 0x5f, 0x5e, 0x8b, 0x3f, 0x72, 0xf7, 0x60, 0x45, 0x93, 0xf8, 0xd4, 0xdb, 0x2c, 0xf4, 0x48, + 0x43, 0x83, 0xf3, 0x68, 0x2e, 0xf0, 0x31, 0x6a, 0xc1, 0x22, 0xf1, 0x83, 0x29, 0x4d, 0x53, 0x88, + 0x85, 0x63, 0x75, 0x2a, 0xdd, 0xc6, 0x60, 0xff, 0x2e, 0x77, 0x9b, 0xe7, 0xdf, 0x5d, 0x0d, 0x4b, + 0x9a, 0x34, 0x61, 0x91, 0x3c, 0x00, 0x7c, 0x85, 0x0e, 0x03, 0x0e, 0x54, 0x82, 0x7f, 0x9b, 0xa5, + 0x52, 0xa9, 0xe6, 0xdf, 0x02, 0x38, 0x0d, 0xad, 0xd5, 0xd3, 0x52, 0xab, 0x0f, 0x02, 0x26, 0x12, + 0x26, 0x44, 0x38, 0xeb, 0x45, 0xac, 0x9f, 0x50, 0x39, 0xed, 0x5d, 0xa6, 0x92, 0x1c, 0x14, 0x95, + 0x17, 0x65, 0xe1, 0x05, 0xc0, 0xa9, 0xf9, 0xfb, 0x1b, 0xd7, 0x18, 0x99, 0x96, 0x61, 0xef, 0x8e, + 0x4c, 0x6b, 0xd7, 0xae, 0x8c, 0x4c, 0xab, 0x62, 0x9b, 0x23, 0xd3, 0xaa, 0xda, 0xb5, 0x91, 0x69, + 0xd5, 0xec, 0xfa, 0xc8, 0xb4, 0xea, 0xb6, 0xe5, 0xf5, 0x51, 0xf5, 0xb5, 0xa4, 0x12, 0xb0, 0x8d, + 0x2a, 0x33, 0x58, 0x15, 0xee, 0x10, 0x15, 0xe2, 0x23, 0x54, 0x5d, 0xd0, 0x38, 0x83, 0x52, 0xda, + 0x02, 0x78, 0x3f, 0xef, 0xa2, 0xca, 0xd7, 0x6c, 0x82, 0x1d, 0x54, 0x57, 0x76, 0x82, 0x10, 0x65, + 0xcd, 0x03, 0xc4, 0x1f, 0xa2, 0x9a, 0x64, 0xf3, 0x28, 0x10, 0xce, 0xae, 0x3a, 0x39, 0x29, 0x11, + 0xc6, 0xc8, 0x0c, 0xa9, 0xa4, 0xda, 0x83, 0x16, 0xd1, 0xb1, 0xd2, 0x6a, 0x1c, 0xb3, 0x60, 0xe6, + 0xa7, 0x59, 0x32, 0x06, 0xee, 0x98, 0x1d, 0xa3, 0x6b, 0x0e, 0xf6, 0xd7, 0xb9, 0xdb, 0xd4, 0xfc, + 0x2b, 0x4d, 0x93, 0x6d, 0x80, 0x9f, 0xa1, 0xba, 0x5c, 0xfa, 0x53, 0x2a, 0xa6, 0x4e, 0x55, 0xeb, + 0x73, 0xb8, 0xce, 0xdd, 0x7d, 0xc9, 0x69, 0x2a, 0x68, 0x20, 0x23, 0x96, 0x7e, 0x49, 0xc5, 0x94, + 0xd4, 0xe4, 0x52, 0x3d, 0x71, 0x1f, 0x59, 0x72, 0xe9, 0x47, 0x69, 0x08, 0x4b, 0xa7, 0xa6, 0xbb, + 0x1f, 0xad, 0x73, 0xd7, 0xde, 0x5a, 0x7e, 0xa9, 0x72, 0xa4, 0x2e, 0x97, 0x3a, 0xc0, 0xcf, 0x10, + 0x2a, 0x46, 0xd2, 0x3b, 0xd4, 0xf5, 0x0e, 0x7b, 0xeb, 0xdc, 0x6d, 0x68, 0x56, 0xf7, 0xde, 0x84, + 0xd8, 0x43, 0xd5, 0xa2, 0xb7, 0xa5, 0x7b, 0xb7, 0xd6, 0xb9, 0x6b, 0xc5, 0x6c, 0x52, 0xf4, 0x2c, + 0x52, 0x4a, 0x2a, 0x0e, 0x09, 0x5b, 0x40, 0xa8, 0x0d, 0xb5, 0xc8, 0x03, 0xf4, 0x28, 0x6a, 0x9e, + 0x05, 0x01, 0x08, 0x71, 0x93, 0xcd, 0x63, 0xf8, 0x0f, 0x4d, 0x8f, 0x51, 0x4b, 0x48, 0xc6, 0xe9, + 0x04, 0xfc, 0x19, 0xac, 0x4a, 0x65, 0x0b, 0x9d, 0x4a, 0xfe, 0x2b, 0x58, 0x09, 0xb2, 0x0d, 0x4e, + 0xcd, 0x1f, 0xde, 0xb8, 0x3b, 0xde, 0x10, 0xb5, 0x6e, 0x38, 0x0d, 0x80, 0x0f, 0x59, 0x7a, 0x1b, + 0x4d, 0xf0, 0x0b, 0xb4, 0xc7, 0xd2, 0x78, 0xe5, 0x4b, 0x36, 0xf7, 0x03, 0x1a, 0xc7, 0x7a, 0x27, + 0xab, 0x68, 0xa5, 0x12, 0x37, 0x6c, 0x3e, 0xa4, 0x71, 0x4c, 0xb6, 0x81, 0xf7, 0x57, 0x05, 0x35, + 0x75, 0x97, 0xb2, 0x89, 0xb2, 0x58, 0x37, 0x2d, 0xe7, 0x2c, 0x91, 0x3a, 0x80, 0x8c, 0x12, 0x60, + 0x99, 0x2c, 0x2f, 0xcd, 0x03, 0x54, 0x15, 0x1c, 0x60, 0x09, 0x81, 0xb6, 0xdf, 0x24, 0x25, 0xc2, + 0x27, 0x68, 0x2f, 0x8c, 0x04, 0x1d, 0xc7, 0xe0, 0x0b, 0x49, 0x83, 0x99, 0xb6, 0xd4, 0x1a, 0xd8, + 0xeb, 0xdc, 0x6d, 0x95, 0x89, 0xd7, 0x8a, 0x27, 0x8f, 0x10, 0xfe, 0x1c, 0xed, 0x6f, 0xca, 0xf4, + 0x91, 0xb5, 0xb9, 0xd6, 0x00, 0xaf, 0x73, 0xf7, 0xc9, 0xfb, 0xa5, 0x3a, 0x43, 0xfe, 0x81, 0xd5, + 0xc5, 0x0e, 0x61, 0x9c, 0x4d, 0xb4, 0x67, 0x16, 0x29, 0x80, 0x62, 0xe3, 0x28, 0x89, 0xa4, 0xf6, + 0xa8, 0x4a, 0x0a, 0xa0, 0xe6, 0x83, 0x54, 0xef, 0x93, 0x40, 0xc2, 0xf8, 0xca, 0x69, 0x6e, 0xe6, + 0x2b, 0x12, 0x57, 0x9a, 0x27, 0x8f, 0x10, 0x1e, 0x20, 0x5c, 0x96, 0x71, 0x90, 0x19, 0x4f, 0x7d, + 0x7d, 0xf3, 0x5b, 0xba, 0x56, 0xdf, 0xbf, 0x22, 0x4b, 0x74, 0xf2, 0x25, 0x95, 0x94, 0xfc, 0x8b, + 0xc1, 0xdf, 0xa0, 0xbd, 0x42, 0x56, 0x3f, 0xd0, 0xaa, 0x3b, 0x7b, 0x1d, 0xa3, 0xdb, 0x3c, 0x76, + 0x7a, 0x9b, 0xaf, 0x6f, 0x6f, 0xdb, 0xda, 0x62, 0x28, 0xb9, 0xc5, 0x90, 0x47, 0x68, 0x64, 0x5a, + 0xa6, 0x5d, 0x2d, 0xde, 0xfb, 0x91, 0x69, 0x21, 0xbb, 0xf9, 0x5e, 0x99, 0xf2, 0x70, 0xe4, 0xf0, + 0x01, 0x6f, 0x4d, 0x3d, 0xf8, 0xe2, 0xed, 0x5d, 0xdb, 0x78, 0x77, 0xd7, 0x36, 0x7e, 0xbb, 0x6b, + 0x1b, 0x3f, 0xde, 0xb7, 0x77, 0xde, 0xdd, 0xb7, 0x77, 0x7e, 0xb9, 0x6f, 0xef, 0x7c, 0xff, 0xf1, + 0xff, 0x7e, 0xbc, 0x97, 0xea, 0xaf, 0x31, 0xae, 0xe9, 0x9f, 0xc2, 0x8b, 0xbf, 0x03, 0x00, 0x00, + 0xff, 0xff, 0x33, 0x26, 0x36, 0x60, 0x4e, 0x06, 0x00, 0x00, +} + +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.ExtraEIPs) != len(that1.ExtraEIPs) { + return false + } + for i := range this.ExtraEIPs { + if this.ExtraEIPs[i] != that1.ExtraEIPs[i] { + return false + } + } + if len(this.EVMChannels) != len(that1.EVMChannels) { + return false + } + for i := range this.EVMChannels { + if this.EVMChannels[i] != that1.EVMChannels[i] { + return false + } + } + if !this.CreateFuntokenFee.Equal(that1.CreateFuntokenFee) { + return false + } + return true +} +func (m *FunToken) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FunToken) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FunToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsMadeFromCoin { + i-- + if m.IsMadeFromCoin { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.BankDenom) > 0 { + i -= len(m.BankDenom) + copy(dAtA[i:], m.BankDenom) + i = encodeVarintEvm(dAtA, i, uint64(len(m.BankDenom))) + i-- + dAtA[i] = 0x12 + } + { + size := m.Erc20Addr.Size() + i -= size + if _, err := m.Erc20Addr.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintEvm(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.CreateFuntokenFee.Size() + i -= size + if _, err := m.CreateFuntokenFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintEvm(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + if len(m.EVMChannels) > 0 { + for iNdEx := len(m.EVMChannels) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.EVMChannels[iNdEx]) + copy(dAtA[i:], m.EVMChannels[iNdEx]) + i = encodeVarintEvm(dAtA, i, uint64(len(m.EVMChannels[iNdEx]))) + i-- + dAtA[i] = 0x42 + } + } + if len(m.ExtraEIPs) > 0 { + dAtA2 := make([]byte, len(m.ExtraEIPs)*10) + var j1 int + for _, num1 := range m.ExtraEIPs { + num := uint64(num1) + for num >= 1<<7 { + dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA2[j1] = uint8(num) + j1++ + } + i -= j1 + copy(dAtA[i:], dAtA2[:j1]) + i = encodeVarintEvm(dAtA, i, uint64(j1)) + i-- + dAtA[i] = 0x22 + } + return len(dAtA) - i, nil +} + +func (m *State) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *State) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *State) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintEvm(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintEvm(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Log) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Log) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Log) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Removed { + i-- + if m.Removed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if m.Index != 0 { + i = encodeVarintEvm(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x40 + } + if len(m.BlockHash) > 0 { + i -= len(m.BlockHash) + copy(dAtA[i:], m.BlockHash) + i = encodeVarintEvm(dAtA, i, uint64(len(m.BlockHash))) + i-- + dAtA[i] = 0x3a + } + if m.TxIndex != 0 { + i = encodeVarintEvm(dAtA, i, uint64(m.TxIndex)) + i-- + dAtA[i] = 0x30 + } + if len(m.TxHash) > 0 { + i -= len(m.TxHash) + copy(dAtA[i:], m.TxHash) + i = encodeVarintEvm(dAtA, i, uint64(len(m.TxHash))) + i-- + dAtA[i] = 0x2a + } + if m.BlockNumber != 0 { + i = encodeVarintEvm(dAtA, i, uint64(m.BlockNumber)) + i-- + dAtA[i] = 0x20 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintEvm(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x1a + } + if len(m.Topics) > 0 { + for iNdEx := len(m.Topics) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Topics[iNdEx]) + copy(dAtA[i:], m.Topics[iNdEx]) + i = encodeVarintEvm(dAtA, i, uint64(len(m.Topics[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintEvm(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AccessTuple) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AccessTuple) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AccessTuple) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.StorageKeys) > 0 { + for iNdEx := len(m.StorageKeys) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.StorageKeys[iNdEx]) + copy(dAtA[i:], m.StorageKeys[iNdEx]) + i = encodeVarintEvm(dAtA, i, uint64(len(m.StorageKeys[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintEvm(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TracerConfig) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TracerConfig) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TracerConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.OnlyTopCall { + i-- + if m.OnlyTopCall { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *TraceConfig) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TraceConfig) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TraceConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TracerConfig != nil { + { + size, err := m.TracerConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvm(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6a + } + if m.EnableReturnData { + i-- + if m.EnableReturnData { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x60 + } + if m.EnableMemory { + i-- + if m.EnableMemory { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x58 + } + if m.Limit != 0 { + i = encodeVarintEvm(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x48 + } + if m.Debug { + i-- + if m.Debug { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.DisableStorage { + i-- + if m.DisableStorage { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if m.DisableStack { + i-- + if m.DisableStack { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Reexec != 0 { + i = encodeVarintEvm(dAtA, i, uint64(m.Reexec)) + i-- + dAtA[i] = 0x18 + } + if len(m.Timeout) > 0 { + i -= len(m.Timeout) + copy(dAtA[i:], m.Timeout) + i = encodeVarintEvm(dAtA, i, uint64(len(m.Timeout))) + i-- + dAtA[i] = 0x12 + } + if len(m.Tracer) > 0 { + i -= len(m.Tracer) + copy(dAtA[i:], m.Tracer) + i = encodeVarintEvm(dAtA, i, uint64(len(m.Tracer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintEvm(dAtA []byte, offset int, v uint64) int { + offset -= sovEvm(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *FunToken) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Erc20Addr.Size() + n += 1 + l + sovEvm(uint64(l)) + l = len(m.BankDenom) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + if m.IsMadeFromCoin { + n += 2 + } + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ExtraEIPs) > 0 { + l = 0 + for _, e := range m.ExtraEIPs { + l += sovEvm(uint64(e)) + } + n += 1 + sovEvm(uint64(l)) + l + } + if len(m.EVMChannels) > 0 { + for _, s := range m.EVMChannels { + l = len(s) + n += 1 + l + sovEvm(uint64(l)) + } + } + l = m.CreateFuntokenFee.Size() + n += 1 + l + sovEvm(uint64(l)) + return n +} + +func (m *State) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + return n +} + +func (m *Log) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + if len(m.Topics) > 0 { + for _, s := range m.Topics { + l = len(s) + n += 1 + l + sovEvm(uint64(l)) + } + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + if m.BlockNumber != 0 { + n += 1 + sovEvm(uint64(m.BlockNumber)) + } + l = len(m.TxHash) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + if m.TxIndex != 0 { + n += 1 + sovEvm(uint64(m.TxIndex)) + } + l = len(m.BlockHash) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + if m.Index != 0 { + n += 1 + sovEvm(uint64(m.Index)) + } + if m.Removed { + n += 2 + } + return n +} + +func (m *AccessTuple) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + if len(m.StorageKeys) > 0 { + for _, s := range m.StorageKeys { + l = len(s) + n += 1 + l + sovEvm(uint64(l)) + } + } + return n +} + +func (m *TracerConfig) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OnlyTopCall { + n += 2 + } + return n +} + +func (m *TraceConfig) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Tracer) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + l = len(m.Timeout) + if l > 0 { + n += 1 + l + sovEvm(uint64(l)) + } + if m.Reexec != 0 { + n += 1 + sovEvm(uint64(m.Reexec)) + } + if m.DisableStack { + n += 2 + } + if m.DisableStorage { + n += 2 + } + if m.Debug { + n += 2 + } + if m.Limit != 0 { + n += 1 + sovEvm(uint64(m.Limit)) + } + if m.EnableMemory { + n += 2 + } + if m.EnableReturnData { + n += 2 + } + if m.TracerConfig != nil { + l = m.TracerConfig.Size() + n += 1 + l + sovEvm(uint64(l)) + } + return n +} + +func sovEvm(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvm(x uint64) (n int) { + return sovEvm(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *FunToken) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FunToken: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FunToken: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Erc20Addr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Erc20Addr.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BankDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BankDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsMadeFromCoin", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsMadeFromCoin = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipEvm(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvm + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 4: + if wireType == 0 { + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ExtraEIPs = append(m.ExtraEIPs, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.ExtraEIPs) == 0 { + m.ExtraEIPs = make([]int64, 0, elementCount) + } + for iNdEx < postIndex { + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ExtraEIPs = append(m.ExtraEIPs, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field ExtraEIPs", wireType) + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EVMChannels", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EVMChannels = append(m.EVMChannels, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreateFuntokenFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.CreateFuntokenFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvm(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvm + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *State) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: State: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: State: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvm(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvm + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Log) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Log: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Log: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Topics", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Topics = append(m.Topics, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockNumber", wireType) + } + m.BlockNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxIndex", wireType) + } + m.TxIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxIndex |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BlockHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Removed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Removed = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipEvm(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvm + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AccessTuple) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AccessTuple: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AccessTuple: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StorageKeys", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.StorageKeys = append(m.StorageKeys, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvm(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvm + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TracerConfig) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TracerConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TracerConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OnlyTopCall", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.OnlyTopCall = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipEvm(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvm + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TraceConfig) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TraceConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TraceConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tracer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tracer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Timeout = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Reexec", wireType) + } + m.Reexec = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Reexec |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DisableStack", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DisableStack = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DisableStorage", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DisableStorage = bool(v != 0) + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Debug", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Debug = bool(v != 0) + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EnableMemory", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EnableMemory = bool(v != 0) + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EnableReturnData", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EnableReturnData = bool(v != 0) + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TracerConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TracerConfig == nil { + m.TracerConfig = &TracerConfig{} + } + if err := m.TracerConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvm(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvm + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvm(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvm + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvm + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvm + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthEvm + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvm + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvm + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvm = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvm = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvm = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/evm/evm_test.go b/x/evm/evm_test.go new file mode 100644 index 000000000..d6eb1f5f1 --- /dev/null +++ b/x/evm/evm_test.go @@ -0,0 +1,203 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm_test + +import ( + "math/big" + "strconv" + "strings" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +type TestSuite struct { + suite.Suite +} + +func TestSuite_RunAll(t *testing.T) { + suite.Run(t, new(TestSuite)) +} + +func (s *TestSuite) TestFunToken() { + for idx, tc := range []struct { + bankDenom string + input string + wantErr bool + }{ + { + // sad: Invalid bank denom + bankDenom: "", + input: "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + wantErr: true, + }, + { + bankDenom: "unibi", + input: "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + }, + { + bankDenom: "unibi", + input: "5AAEB6053F3E94C9B9A09F33669435E7EF1BEAED", + }, + { + bankDenom: "unibi", + input: "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + }, + + { + bankDenom: "ibc/AAA/BBB", + input: "0xE1aA1500b962528cBB42F05bD6d8A6032a85602f", + }, + { + bankDenom: "tf/contract-addr/subdenom", + input: "0x6B2e60f1030aFa69F584829f1d700b47eE5Fc74a", + }, + } { + s.Run(strconv.Itoa(idx), func() { + eip55Addr, err := eth.NewEIP55AddrFromStr(tc.input) + s.Require().NoError(err) + + funtoken := evm.FunToken{ + Erc20Addr: eip55Addr, + BankDenom: tc.bankDenom, + } + if tc.wantErr { + s.Require().Error(funtoken.Validate()) + return + } + + s.Require().NoError(funtoken.Validate()) + }) + } + + for _, tc := range []struct { + name string + A string + B string + }{ + { + name: "capital and lowercase match", + A: "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + B: "5AAEB6053F3E94C9B9A09F33669435E7EF1BEAED", + }, + { + name: "0x prefix and no prefix match", + A: "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + B: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + }, + { + name: "0x prefix and no prefix match", + A: "5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + B: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + }, + { + name: "mixed case compatibility", + A: "0x5Bdb32670a05Daa22Cb2E279B80044c37dc85e61", + B: "0x5BDB32670A05DAA22CB2E279B80044C37DC85E61", + }, + } { + s.Run(tc.name, func() { + addrA, err := eth.NewEIP55AddrFromStr(tc.A) + s.Require().NoError(err) + + addrB, err := eth.NewEIP55AddrFromStr(tc.B) + s.Require().NoError(err) + + funA := evm.FunToken{Erc20Addr: addrA} + funB := evm.FunToken{Erc20Addr: addrB} + + s.EqualValues(funA.Erc20Addr.Address, funB.Erc20Addr.Address) + }) + } +} + +func (s *TestSuite) TestModuleAddressEVM() { + addr := evm.EVM_MODULE_ADDRESS + s.Equal(addr.Hex(), "0x603871c2ddd41c26Ee77495E2E31e6De7f9957e0") + + // Sanity check + nibiAddr := authtypes.NewModuleAddress(evm.ModuleName) + evmModuleAddr := gethcommon.BytesToAddress(nibiAddr) + s.Equal(addr.Hex(), evmModuleAddr.Hex()) + + // EVM addr module acc and EVM address should be connected + // EVM module should have mint perms + deps := evmtest.NewTestDeps() + { + resp, err := deps.EvmKeeper.EthAccount(sdk.WrapSDKContext(deps.Ctx), &evm.QueryEthAccountRequest{ + Address: evmModuleAddr.Hex(), + }) + s.NoError(err) + s.Equal(nibiAddr.String(), resp.Bech32Address) + s.Equal(evmModuleAddr.String(), resp.EthAddress) + } +} + +func (s *TestSuite) TestWeiConversion() { + { + unibiAmt := big.NewInt(420) + s.Equal( + unibiAmt, + evm.WeiToNative(evm.NativeToWei(unibiAmt)), + "native -> wei -> native should be an identity operation", + ) + + weiAmt := evm.NativeToWei(unibiAmt) + want := "420" + strings.Repeat("0", 12) + s.Equal(weiAmt.String(), want) + } + + tenPow12 := new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil) + for _, tc := range []struct { + weiAmtIn string + want *big.Int + wantError string + }{ + { + // Input number: 123456789012345678901234567890 + // Parsed number: 123456789012345678 * 10^12 + weiAmtIn: "123456789012345678901234567890", + want: evm.NativeToWei(big.NewInt(123456789012345678)), + wantError: "", + }, + { + weiAmtIn: "123456789012345678901234567890", + want: evm.NativeToWei(big.NewInt(123456789012345678)), + wantError: "", + }, + { + weiAmtIn: "0", + want: big.NewInt(0), + wantError: "", + }, + { + weiAmtIn: "1", + wantError: "cannot transfer less than 1 micronibi.", + }, + { + weiAmtIn: new(big.Int).Sub( + tenPow12, big.NewInt(1), + ).String(), + wantError: "cannot transfer less than 1 micronibi.", + }, + { + weiAmtIn: "500", + wantError: "cannot transfer less than 1 micronibi.", + }, + } { + weiAmtIn, _ := new(big.Int).SetString(tc.weiAmtIn, 10) + got, err := evm.ParseWeiAsMultipleOfMicronibi(weiAmtIn) + if tc.wantError != "" { + s.Require().ErrorContains(err, tc.wantError) + return + } + s.NoError(err) + s.Require().Equal(tc.want.String(), got.String()) + } +} diff --git a/x/evm/evmmodule/genesis.go b/x/evm/evmmodule/genesis.go new file mode 100644 index 000000000..b552b25ea --- /dev/null +++ b/x/evm/evmmodule/genesis.go @@ -0,0 +1,129 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmmodule + +import ( + "bytes" + "fmt" + + "github.com/NibiruChain/collections" + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" +) + +// InitGenesis initializes genesis state based on exported genesis +func InitGenesis( + ctx sdk.Context, + k *keeper.Keeper, + accountKeeper evm.AccountKeeper, + genState evm.GenesisState, +) []abci.ValidatorUpdate { + err := k.SetParams(ctx, genState.Params) + if err != nil { + panic(fmt.Errorf("failed to set params: %w", err)) + } + + // Note that "GetModuleAccount" initializes the module account with permissions + // under the hood if it did not already exist. This is important because the + // EVM module needs to be able to send and receive funds during MsgEthereumTx + if evmModule := accountKeeper.GetModuleAccount(ctx, evm.ModuleName); evmModule == nil { + panic("the EVM module account has not been set") + } + + // Create evm contracts from genstate.Accounts + for _, account := range genState.Accounts { + address := gethcommon.HexToAddress(account.Address) + accAddress := sdk.AccAddress(address.Bytes()) + // check that the EVM balance the matches the account balance + acc := accountKeeper.GetAccount(ctx, accAddress) + if acc == nil { + panic(fmt.Errorf("account not found for address %s", account.Address)) + } + + ethAcct, ok := acc.(eth.EthAccountI) + if !ok { + panic( + fmt.Errorf("account %s must be an EthAccount interface, got %T", + account.Address, acc, + ), + ) + } + code := gethcommon.Hex2Bytes(account.Code) + codeHash := crypto.Keccak256Hash(code) + + // we ignore the empty Code hash checking, see ethermint PR#1234 + if len(account.Code) != 0 && !bytes.Equal(ethAcct.GetCodeHash().Bytes(), codeHash.Bytes()) { + s := "the evm state code doesn't match with the codehash\n" + panic(fmt.Sprintf("%s account: %s , evm state codehash: %v, ethAccount codehash: %v, evm state code: %s\n", + s, account.Address, codeHash, ethAcct.GetCodeHash(), account.Code)) + } + k.SetCode(ctx, codeHash.Bytes(), code) + + for _, storage := range account.Storage { + k.SetState(ctx, address, gethcommon.HexToHash(storage.Key), gethcommon.HexToHash(storage.Value).Bytes()) + } + } + + // Create fungible token mappings + for _, funToken := range genState.FuntokenMappings { + err := k.FunTokens.SafeInsert( + ctx, gethcommon.HexToAddress(funToken.Erc20Addr.String()), funToken.BankDenom, funToken.IsMadeFromCoin, + ) + if err != nil { + panic(fmt.Errorf("failed creating funtoken: %w", err)) + } + } + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis exports genesis state of the EVM module +func ExportGenesis(ctx sdk.Context, k *keeper.Keeper, ak evm.AccountKeeper) *evm.GenesisState { + var genesisAccounts []evm.GenesisAccount + + // 1. Export EVM contacts + // TODO: find the way to get eth contract addresses from the evm keeper + allAccounts := ak.GetAllAccounts(ctx) + for _, acc := range allAccounts { + ethAcct, ok := acc.(eth.EthAccountI) + if ok { + address := ethAcct.EthAddress() + codeHash := ethAcct.GetCodeHash() + code, err := k.EvmState.ContractBytecode.Get(ctx, codeHash.Bytes()) + if err != nil { + // Not a contract + continue + } + var storage evm.Storage + + k.ForEachStorage(ctx, address, func(key, value gethcommon.Hash) bool { + storage = append(storage, evm.NewStateFromEthHashes(key, value)) + return true + }) + genesisAccounts = append(genesisAccounts, evm.GenesisAccount{ + Address: address.String(), + Code: eth.BytesToHex(code), + Storage: storage, + }) + } + } + + // 2. Export Fungible tokens + var funTokens []evm.FunToken + iter := k.FunTokens.Iterate(ctx, collections.Range[[]byte]{}) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + funTokens = append(funTokens, iter.Value()) + } + + return &evm.GenesisState{ + Params: k.GetParams(ctx), + Accounts: genesisAccounts, + FuntokenMappings: funTokens, + } +} diff --git a/x/evm/evmmodule/genesis_test.go b/x/evm/evmmodule/genesis_test.go new file mode 100644 index 000000000..a0a9b6d23 --- /dev/null +++ b/x/evm/evmmodule/genesis_test.go @@ -0,0 +1,129 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmmodule_test + +import ( + "math/big" + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmmodule" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +type Suite struct { + suite.Suite +} + +// TestKeeperSuite: Runs all the tests in the suite. +func TestKeeperSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} + +// TestExportInitGenesis +// - creates evm state with erc20 contract, sends tokens to user A and B +// - creates fungible token from unibi coin and sends to user C +// - exports / imports genesis +// - verifies that contracts are in place and user balances match +// - verifies that fungible token is in place and the balance is correct +func (s *Suite) TestExportInitGenesis() { + deps := evmtest.NewTestDeps() + erc20Contract := embeds.SmartContract_TestERC20 + fromUser := deps.Sender.EthAddr + toUserA := gethcommon.HexToAddress("0xAE8A5F44A9b55Ae6D2c9C228253E8fAfb837d2F2") + toUserB := gethcommon.HexToAddress("0xf893292542F2578F1004e62fd723901ddE5EC5Cf") + toUserC := gethcommon.HexToAddress("0xe90f75496E744b92B52535bB05a29123D0D94D49") + amountToSendA := big.NewInt(1550) + amountToSendB := big.NewInt(333) + amountToSendC := big.NewInt(228) + + // Create ERC-20 contract + deployResp, err := evmtest.DeployContract(&deps, erc20Contract) + s.Require().NoError(err) + erc20Addr := deployResp.ContractAddr + + evmObj, _ := deps.NewEVM() + totalSupply, err := deps.EvmKeeper.ERC20().LoadERC20BigInt( + deps.Ctx, evmObj, erc20Contract.ABI, erc20Addr, "totalSupply", + ) + s.Require().NoError(err) + + // Transfer ERC-20 tokens to user A + _, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, fromUser, toUserA, amountToSendA, deps.Ctx, evmObj) + s.Require().NoError(err) + + // Transfer ERC-20 tokens to user B + _, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, fromUser, toUserB, amountToSendB, deps.Ctx, evmObj) + s.Require().NoError(err) + + // Create fungible token from bank coin + funToken := evmtest.CreateFunTokenForBankCoin(deps, "unibi", &s.Suite) + s.Require().NoError(err) + funTokenAddr := funToken.Erc20Addr.Address + + // Fund sender's wallet + spendableCoins := sdk.NewCoins(sdk.NewInt64Coin("unibi", totalSupply.Int64())) + err = deps.App.BankKeeper.MintCoins(deps.Ctx, evm.ModuleName, spendableCoins) + s.Require().NoError(err) + err = deps.App.BankKeeper.SendCoinsFromModuleToAccount( + deps.Ctx, evm.ModuleName, deps.Sender.NibiruAddr, spendableCoins, + ) + s.Require().NoError(err) + + eip55Addr, err := eth.NewEIP55AddrFromStr(toUserC.String()) + s.Require().NoError(err) + // Send fungible token coins from bank to evm + _, err = deps.EvmKeeper.ConvertCoinToEvm( + deps.Ctx, + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.Coin{Denom: "unibi", Amount: math.NewInt(amountToSendC.Int64())}, + ToEthAddr: eip55Addr, + }, + ) + s.Require().NoError(err) + + // Export genesis + evmGenesisState := evmmodule.ExportGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper) + authGenesisState := deps.App.AccountKeeper.ExportGenesis(deps.Ctx) + + // Init genesis from the exported state + deps = evmtest.NewTestDeps() + deps.App.AccountKeeper.InitGenesis(deps.Ctx, *authGenesisState) + evmmodule.InitGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper, *evmGenesisState) + + // Verify erc20 balances for users A, B and sender + balance, err := deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserA, deps.Ctx, evmObj) + s.Require().NoError(err) + s.Require().Equal(amountToSendA, balance) + + balance, err = deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserB, deps.Ctx, evmObj) + s.Require().NoError(err) + s.Require().Equal(amountToSendB, balance) + + balance, err = deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, fromUser, deps.Ctx, evmObj) + s.Require().NoError(err) + s.Require().Equal( + new(big.Int).Sub(totalSupply, big.NewInt(amountToSendA.Int64()+amountToSendB.Int64())), + balance, + ) + + // Check that fungible token mapping is in place + iter := deps.EvmKeeper.FunTokens.Indexes.BankDenom.ExactMatch(deps.Ctx, "unibi") + funTokens := deps.EvmKeeper.FunTokens.Collect(deps.Ctx, iter) + s.Require().Len(funTokens, 1) + s.Require().Equal(funTokenAddr.String(), funTokens[0].Erc20Addr.String()) + s.Require().Equal("unibi", funTokens[0].BankDenom) + s.Require().True(funTokens[0].IsMadeFromCoin) + + // Check that fungible token balance of user C is correct + balance, err = deps.EvmKeeper.ERC20().BalanceOf(funTokenAddr, toUserC, deps.Ctx, evmObj) + s.Require().NoError(err) + s.Require().Equal(amountToSendC, balance) +} diff --git a/x/evm/evmmodule/module.go b/x/evm/evmmodule/module.go new file mode 100644 index 000000000..5c297688d --- /dev/null +++ b/x/evm/evmmodule/module.go @@ -0,0 +1,172 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmmodule + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/cli" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" +) + +// consensusVersion: EVM module consensus version for upgrades. +const consensusVersion = 1 + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.EndBlockAppModule = AppModule{} + _ module.BeginBlockAppModule = AppModule{} +) + +// AppModuleBasic defines the basic application module used by the evm module. +type AppModuleBasic struct{} + +// Name returns the evm module's name. +func (AppModuleBasic) Name() string { + return evm.ModuleName +} + +// RegisterLegacyAminoCodec registers the module's types with the given codec. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + evm.RegisterLegacyAminoCodec(cdc) +} + +// ConsensusVersion returns the consensus state-breaking version for the module. +func (AppModuleBasic) ConsensusVersion() uint64 { + return consensusVersion +} + +// DefaultGenesis returns default genesis state as raw bytes for the evm +// module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(evm.DefaultGenesisState()) +} + +// ValidateGenesis is the validation check of the Genesis +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var genesisState evm.GenesisState + if err := cdc.UnmarshalJSON(bz, &genesisState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", evm.ModuleName, err) + } + + return genesisState.Validate() +} + +// RegisterRESTRoutes performs a no-op as the EVM module doesn't expose REST +// endpoints +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) { +} + +func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c client.Context, serveMux *runtime.ServeMux) { + if err := evm.RegisterQueryHandlerClient(context.Background(), serveMux, evm.NewQueryClient(c)); err != nil { + panic(err) + } +} + +// GetTxCmd returns the root tx command for the evm module. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.GetTxCmd() +} + +// GetQueryCmd returns no root query command for the evm module. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterInterfaces registers interfaces and implementations of the evm module. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + evm.RegisterInterfaces(registry) + eth.RegisterInterfaces(registry) +} + +// ____________________________________________________________________________ + +// AppModule implements an application module for the evm module. +type AppModule struct { + AppModuleBasic + keeper *keeper.Keeper + ak evm.AccountKeeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(k *keeper.Keeper, ak evm.AccountKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: k, + ak: ak, + } +} + +// Name returns the evm module's name. +func (AppModule) Name() string { + return evm.ModuleName +} + +// RegisterInvariants interface for registering invariants. Performs a no-op +// as the evm module doesn't expose invariants. +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) { +} + +// RegisterServices registers a GRPC query service to respond to the +// module-specific GRPC queries. +func (am AppModule) RegisterServices(cfg module.Configurator) { + evm.RegisterMsgServer(cfg.MsgServer(), am.keeper) + evm.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// BeginBlock returns the begin block for the evm module. +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { + am.keeper.BeginBlock(ctx, req) +} + +// EndBlock returns the end blocker for the evm module. It returns no validator +// updates. +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return am.keeper.EndBlock(ctx, req) +} + +// InitGenesis performs genesis initialization for the evm module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState evm.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + InitGenesis(ctx, am.keeper, am.ak, genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the evm +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + gs := ExportGenesis(ctx, am.keeper, am.ak) + return cdc.MustMarshalJSON(gs) +} + +// RegisterStoreDecoder registers a decoder for evm module's types +func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) { +} + +// GenerateGenesisState creates a randomized GenState of the evm module. +func (AppModule) GenerateGenesisState(_ *module.SimulationState) { +} + +// WeightedOperations returns the all the evm module operations with their respective weights. +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + return nil +} diff --git a/x/evm/evmtest/erc20.go b/x/evm/evmtest/erc20.go new file mode 100644 index 000000000..feb426209 --- /dev/null +++ b/x/evm/evmtest/erc20.go @@ -0,0 +1,158 @@ +package evmtest + +import ( + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +func AssertERC20BalanceEqualWithDescription( + t *testing.T, + deps TestDeps, + evmObj *vm.EVM, + erc20, account gethcommon.Address, + expectedBalance *big.Int, + description string, +) { + actualBalance, err := deps.EvmKeeper.ERC20().BalanceOf(erc20, account, deps.Ctx, evmObj) + var errSuffix string + if description == "" { + errSuffix = description + } else { + errSuffix = ": " + description + } + assert.NoError(t, err, errSuffix) + assert.Equalf(t, expectedBalance.String(), actualBalance.String(), + "expected %s, got %s: %s", expectedBalance, actualBalance, errSuffix, + ) +} + +func AssertBankBalanceEqualWithDescription( + t *testing.T, + deps TestDeps, + denom string, + account gethcommon.Address, + expectedBalance *big.Int, + description string, +) { + bech32Addr := eth.EthAddrToNibiruAddr(account) + actualBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, bech32Addr, denom).Amount.BigInt() + var errSuffix string + if description == "" { + errSuffix = description + } else { + errSuffix = ": " + description + } + assert.Equalf(t, expectedBalance.String(), actualBalance.String(), + "expected %s, got %s: %s", expectedBalance, actualBalance, errSuffix, + ) +} + +// CreateFunTokenForBankCoin: Uses the "TestDeps.Sender" account to create a +// "FunToken" mapping for a new coin +func CreateFunTokenForBankCoin( + deps TestDeps, bankDenom string, s *suite.Suite, +) (funtoken evm.FunToken) { + if deps.App.BankKeeper.HasDenomMetaData(deps.Ctx, bankDenom) { + s.Failf("setting bank.DenomMetadata would overwrite existing denom \"%s\"", bankDenom) + } + + s.T().Log("Setup: Create a coin in the bank state") + bankMetadata := bank.Metadata{ + DenomUnits: []*bank.DenomUnit{ + { + Denom: bankDenom, + Exponent: 0, + }, + }, + Base: bankDenom, + Display: bankDenom, + Name: bankDenom, + Symbol: bankDenom, + } + + deps.App.BankKeeper.SetDenomMetaData(deps.Ctx, bankMetadata) + + // Give the sender funds for the fee + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + s.T().Log("happy: CreateFunToken for the bank coin") + createFuntokenResp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: bankDenom, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.NoError(err, "bankDenom %s", bankDenom) + + erc20 := createFuntokenResp.FuntokenMapping.Erc20Addr + funtoken = evm.FunToken{ + Erc20Addr: erc20, + BankDenom: bankDenom, + IsMadeFromCoin: true, + } + s.Equal(funtoken, createFuntokenResp.FuntokenMapping) + + s.T().Log("Expect ERC20 to be deployed") + _, err = deps.EvmKeeper.Code(deps.Ctx, + &evm.QueryCodeRequest{ + Address: erc20.String(), + }, + ) + s.NoError(err) + + return funtoken +} + +// BigPow multiplies "amount" by 10 to the "pow10Exp". +func BigPow(amount *big.Int, pow10Exp uint8) (powAmount *big.Int) { + pow10 := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(pow10Exp)), nil) + return new(big.Int).Mul(amount, pow10) +} + +type FunTokenBalanceAssert struct { + FunToken evm.FunToken + Account gethcommon.Address + BalanceBank *big.Int + BalanceERC20 *big.Int + Description string +} + +func (bals FunTokenBalanceAssert) Assert(t *testing.T, deps TestDeps, evmObj *vm.EVM) { + AssertERC20BalanceEqualWithDescription( + t, deps, evmObj, bals.FunToken.Erc20Addr.Address, bals.Account, bals.BalanceERC20, + bals.Description, + ) + AssertBankBalanceEqualWithDescription( + t, deps, bals.FunToken.BankDenom, bals.Account, bals.BalanceBank, + bals.Description, + ) +} + +const ( + // FunTokenGasLimitSendToEvm consists of gas for 3 calls: + // 1. transfer erc20 from sender to module + // ~60_000 gas for regular erc20 transfer (our own ERC20Minter contract) + // could be higher for user created contracts, let's cap with 200_000 + // 2. mint native coin (made from erc20) or burn erc20 token (made from coin) + // ~60_000 gas for either mint or burn + // 3. send from module to account: + // ~65_000 gas (bank send) + FunTokenGasLimitSendToEvm uint64 = 400_000 +) diff --git a/x/evm/evmtest/eth.go b/x/evm/evmtest/eth.go new file mode 100644 index 000000000..f6037cfc5 --- /dev/null +++ b/x/evm/evmtest/eth.go @@ -0,0 +1,100 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmtest + +import ( + "math/big" + "testing" + + cmt "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/stretchr/testify/assert" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" + + "github.com/cosmos/cosmos-sdk/client" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// NewEthPrivAcc returns an Ethereum private key, its corresponding Eth address, Nibiru address, and keyring signer. +func NewEthPrivAcc() EthPrivKeyAcc { + privkey, _ := ethsecp256k1.GenerateKey() + privKeyE, _ := privkey.ToECDSA() + ethAddr := crypto.PubkeyToAddress(privKeyE.PublicKey) + return EthPrivKeyAcc{ + EthAddr: ethAddr, + NibiruAddr: eth.EthAddrToNibiruAddr(ethAddr), + PrivKey: privkey, + KeyringSigner: NewSigner(privkey), + } +} + +// NewEthPrivAccs calls [NewEthAccInfo] n times. +func NewEthPrivAccs(n int) []EthPrivKeyAcc { + infos := make([]EthPrivKeyAcc, n) + for idx := 0; idx < n; idx++ { + infos[idx] = NewEthPrivAcc() + } + return infos +} + +type EthPrivKeyAcc struct { + EthAddr gethcommon.Address + NibiruAddr sdk.AccAddress + PrivKey *ethsecp256k1.PrivKey + KeyringSigner keyring.Signer +} + +func NewEthTxMsgs(count uint64) (ethTxMsgs []*evm.MsgEthereumTx) { + ethAddr := NewEthPrivAcc().EthAddr + startIdx := uint64(1) + for nonce := startIdx; nonce-startIdx < count; nonce++ { + ethTxMsgs = append(ethTxMsgs, evm.NewTx(&evm.EvmTxArgs{ + ChainID: big.NewInt(1), + Nonce: nonce, + To: ðAddr, + GasLimit: 100000, + GasPrice: big.NewInt(1), + Input: []byte("testinput"), + Accesses: &gethcore.AccessList{}, + }), + ) + } + return ethTxMsgs +} + +// NewEthTxMsgAsCmt: Helper that returns an Ethereum tx msg converted into +// tx bytes used in the Consensus Engine. +func NewEthTxMsgAsCmt(t *testing.T) ( + txBz cmt.Tx, + ethTxMsgs []*evm.MsgEthereumTx, + clientCtx client.Context, +) { + // Build a TxBuilder that can understand Ethereum types + encCfg := app.MakeEncodingConfig() + evm.RegisterInterfaces(encCfg.InterfaceRegistry) + eth.RegisterInterfaces(encCfg.InterfaceRegistry) + txConfig := encCfg.TxConfig + clientCtx = client.Context{ + TxConfig: txConfig, + InterfaceRegistry: encCfg.InterfaceRegistry, + } + txBuilder := clientCtx.TxConfig.NewTxBuilder() + + // Build a consensus tx with a few Eth tx msgs + ethTxMsgs = NewEthTxMsgs(3) + assert.NoError(t, + txBuilder.SetMsgs(ethTxMsgs[0], ethTxMsgs[1], ethTxMsgs[2]), + ) + tx := txBuilder.GetTx() + txBz, err := clientCtx.TxConfig.TxEncoder()(tx) + assert.NoError(t, err) + return txBz, ethTxMsgs, clientCtx +} diff --git a/x/evm/evmtest/eth_test.go b/x/evm/evmtest/eth_test.go new file mode 100644 index 000000000..acd49ddfd --- /dev/null +++ b/x/evm/evmtest/eth_test.go @@ -0,0 +1,28 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmtest_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +type Suite struct { + suite.Suite +} + +func TestSuiteEVM(t *testing.T) { + suite.Run(t, new(Suite)) +} + +func (s *Suite) TestSampleFns() { + s.T().Log("Test NewEthTxMsgs") + for _, ethTxMsg := range evmtest.NewEthTxMsgs(3) { + s.NoError(ethTxMsg.ValidateBasic()) + } + + s.T().Log("Test NewEthTxMsgAsCmt") + _, _, _ = evmtest.NewEthTxMsgAsCmt(s.T()) +} diff --git a/x/evm/evmtest/evmante.go b/x/evm/evmtest/evmante.go new file mode 100644 index 000000000..6a72f3718 --- /dev/null +++ b/x/evm/evmtest/evmante.go @@ -0,0 +1,87 @@ +package evmtest + +import ( + "math/big" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +var NextNoOpAnteHandler sdk.AnteHandler = func( + ctx sdk.Context, tx sdk.Tx, simulate bool, +) (newCtx sdk.Context, err error) { + return ctx, nil +} + +func HappyTransferTx(deps *TestDeps, nonce uint64) *evm.MsgEthereumTx { + to := NewEthPrivAcc().EthAddr + evmTxArgs := &evm.EvmTxArgs{ + ChainID: deps.App.EvmKeeper.EthChainID(deps.Ctx), + Nonce: nonce, + Amount: big.NewInt(10), + GasLimit: GasLimitCreateContract().Uint64(), + GasPrice: evm.NativeToWei(big.NewInt(1)), + To: &to, + } + tx := evm.NewTx(evmTxArgs) + tx.From = deps.Sender.EthAddr.Hex() + return tx +} + +func NonEvmMsgTx(deps *TestDeps) sdk.Tx { + gasLimit := uint64(10) + fees := sdk.NewCoins(sdk.NewInt64Coin("unibi", int64(gasLimit))) + msg := &banktypes.MsgSend{ + FromAddress: deps.Sender.NibiruAddr.String(), + ToAddress: NewEthPrivAcc().NibiruAddr.String(), + Amount: sdk.NewCoins(sdk.NewInt64Coin("unibi", 1)), + } + return BuildTx(deps, true, gasLimit, fees, msg) +} + +func BuildTx( + deps *TestDeps, + ethExtentions bool, + gasLimit uint64, + fees sdk.Coins, + msgs ...sdk.Msg, +) sdk.FeeTx { + txBuilder, _ := deps.EncCfg.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder) + if ethExtentions { + option, _ := codectypes.NewAnyWithValue(&evm.ExtensionOptionsEthereumTx{}) + txBuilder.SetExtensionOptions(option) + } + err := txBuilder.SetMsgs(msgs...) + if err != nil { + panic(err) + } + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetFeeAmount(fees) + + return txBuilder.GetTx() +} + +func HappyCreateContractTx(deps *TestDeps) *evm.MsgEthereumTx { + evmTxArgs := &evm.EvmTxArgs{ + ChainID: deps.App.EvmKeeper.EthChainID(deps.Ctx), + Nonce: 1, + Amount: big.NewInt(10), + GasLimit: GasLimitCreateContract().Uint64(), + GasPrice: evm.NativeToWei(big.NewInt(1)), + To: nil, + } + tx := evm.NewTx(evmTxArgs) + tx.From = deps.Sender.EthAddr.Hex() + return tx +} + +func GasLimitCreateContract() *big.Int { + return new(big.Int).SetUint64( + gethparams.TxGasContractCreation + 700, + ) +} diff --git a/x/evm/evmtest/signer.go b/x/evm/evmtest/signer.go new file mode 100644 index 000000000..c05e16afc --- /dev/null +++ b/x/evm/evmtest/signer.go @@ -0,0 +1,52 @@ +package evmtest + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" +) + +var _ keyring.Signer = &Signer{} + +// Signer defines a type that is used on testing for signing MsgEthereumTx +type Signer struct { + privKey cryptotypes.PrivKey +} + +func NewSigner(sk cryptotypes.PrivKey) keyring.Signer { + return &Signer{ + privKey: sk, + } +} + +// Sign signs the message using the underlying private key +func (s Signer) Sign(_ string, msg []byte) ([]byte, cryptotypes.PubKey, error) { + if s.privKey.Type() != ethsecp256k1.KeyType { + return nil, nil, fmt.Errorf( + "invalid private key type for signing ethereum tx; expected %s, got %s", + ethsecp256k1.KeyType, + s.privKey.Type(), + ) + } + + sig, err := s.privKey.Sign(msg) + if err != nil { + return nil, nil, err + } + + return sig, s.privKey.PubKey(), nil +} + +// SignByAddress sign byte messages with a user key providing the address. +func (s Signer) SignByAddress(address sdk.Address, msg []byte) ([]byte, cryptotypes.PubKey, error) { + signer := sdk.AccAddress(s.privKey.PubKey().Address()) + if !signer.Equals(address) { + return nil, nil, fmt.Errorf("address mismatch: signer %s ≠ given address %s", signer, address) + } + + return s.Sign("", msg) +} diff --git a/x/evm/evmtest/smart_contract.go b/x/evm/evmtest/smart_contract.go new file mode 100644 index 000000000..9c9d32cd2 --- /dev/null +++ b/x/evm/evmtest/smart_contract.go @@ -0,0 +1,88 @@ +package evmtest + +import ( + "math/big" + + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// ArgsCreateContract: Arguments to call with `CreateContractTxMsg` to make Ethereum transactions that create +// contracts. +// +// It is recommended to use a gas price of `big.NewInt(1)` for simpler op code +// calculations in gas units. +type ArgsCreateContract struct { + EthAcc EthPrivKeyAcc + EthChainIDInt *big.Int + GasPrice *big.Int + Nonce uint64 + GasLimit *big.Int +} + +// ArgsExecuteContract: Arguments to call with `ExecuteContractTxMsg` +// to make Ethereum transactions that execute contracts. +type ArgsExecuteContract struct { + EthAcc EthPrivKeyAcc + EthChainIDInt *big.Int + ContractAddress *gethcommon.Address + Data []byte + GasPrice *big.Int + Nonce uint64 + GasLimit *big.Int +} + +func CreateContractMsgEthereumTx( + args ArgsCreateContract, +) (msgEthereumTx *evm.MsgEthereumTx, err error) { + gasLimit := args.GasLimit + if gasLimit == nil { + gasLimit = new(big.Int).SetUint64(gethparams.TxGasContractCreation) + } + ethTx := gethcore.NewTx(&gethcore.AccessListTx{ + GasPrice: args.GasPrice, + Gas: gasLimit.Uint64(), + To: nil, + Data: embeds.SmartContract_TestERC20.Bytecode, + Nonce: args.Nonce, + }) + + msgEthereumTx = new(evm.MsgEthereumTx) + err = msgEthereumTx.FromEthereumTx(ethTx) + if err != nil { + return msgEthereumTx, err + } + msgEthereumTx.From = args.EthAcc.EthAddr.Hex() + + gethSigner := gethcore.LatestSignerForChainID(args.EthChainIDInt) + return msgEthereumTx, msgEthereumTx.Sign(gethSigner, args.EthAcc.KeyringSigner) +} + +func ExecuteContractMsgEthereumTx(args ArgsExecuteContract) (msgEthereumTx *evm.MsgEthereumTx, err error) { + gasLimit := args.GasLimit + if gasLimit == nil { + gasLimit = new(big.Int).SetUint64(gethparams.TxGas) + } + + coreTx := gethcore.NewTx(&gethcore.AccessListTx{ + GasPrice: args.GasPrice, + Gas: gasLimit.Uint64(), + To: args.ContractAddress, + Data: args.Data, + Nonce: args.Nonce, + }) + msgEthereumTx = new(evm.MsgEthereumTx) + err = msgEthereumTx.FromEthereumTx(coreTx) + if err != nil { + return msgEthereumTx, err + } + msgEthereumTx.From = args.EthAcc.EthAddr.Hex() + + gethSigner := gethcore.LatestSignerForChainID(args.EthChainIDInt) + return msgEthereumTx, msgEthereumTx.Sign(gethSigner, args.EthAcc.KeyringSigner) +} diff --git a/x/evm/evmtest/smart_contract_test.go b/x/evm/evmtest/smart_contract_test.go new file mode 100644 index 000000000..a78dbcf8e --- /dev/null +++ b/x/evm/evmtest/smart_contract_test.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmtest_test + +import ( + "math/big" + + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *Suite) TestCreateContractTxMsg() { + deps := evmtest.NewTestDeps() + ethAcc := evmtest.NewEthPrivAcc() + + args := evmtest.ArgsCreateContract{ + EthAcc: ethAcc, + EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), + GasPrice: big.NewInt(1), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), + } + + ethTxMsg, err := evmtest.CreateContractMsgEthereumTx(args) + s.NoError(err) + s.Require().NoError(ethTxMsg.ValidateBasic()) +} + +func (s *Suite) TestExecuteContractTxMsg() { + deps := evmtest.NewTestDeps() + ethAcc := evmtest.NewEthPrivAcc() + contractAddress := gethcommon.HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed") + args := evmtest.ArgsExecuteContract{ + EthAcc: ethAcc, + EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), + GasPrice: big.NewInt(1), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), + ContractAddress: &contractAddress, + Data: nil, + } + + ethTxMsg, err := evmtest.ExecuteContractMsgEthereumTx(args) + s.NoError(err) + s.Require().NoError(ethTxMsg.ValidateBasic()) +} diff --git a/x/evm/evmtest/test_deps.go b/x/evm/evmtest/test_deps.go new file mode 100644 index 000000000..36df35d8f --- /dev/null +++ b/x/evm/evmtest/test_deps.go @@ -0,0 +1,67 @@ +package evmtest + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/codec" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +type TestDeps struct { + App *app.NibiruApp + Ctx sdk.Context + EncCfg codec.EncodingConfig + EvmKeeper *keeper.Keeper + GenState *evm.GenesisState + Sender EthPrivKeyAcc +} + +func NewTestDeps() TestDeps { + testapp.EnsureNibiruPrefix() + encCfg := app.MakeEncodingConfig() + evm.RegisterInterfaces(encCfg.InterfaceRegistry) + eth.RegisterInterfaces(encCfg.InterfaceRegistry) + app, ctx := testapp.NewNibiruTestAppAndContext() + ctx = ctx.WithChainID(eth.EIP155ChainID_Testnet) + return TestDeps{ + App: app, + Ctx: ctx, + EncCfg: encCfg, + EvmKeeper: app.EvmKeeper, + GenState: evm.DefaultGenesisState(), + Sender: NewEthPrivAcc(), + } +} + +func (deps TestDeps) NewStateDB() *statedb.StateDB { + return deps.EvmKeeper.NewStateDB( + deps.Ctx, + statedb.NewEmptyTxConfig( + gethcommon.BytesToHash(deps.Ctx.HeaderHash()), + ), + ) +} + +func (deps TestDeps) NewEVM() (*vm.EVM, *statedb.StateDB) { + stateDB := deps.EvmKeeper.NewStateDB(deps.Ctx, statedb.NewEmptyTxConfig(gethcommon.BytesToHash(deps.Ctx.HeaderHash()))) + evmObj := deps.EvmKeeper.NewEVM(deps.Ctx, MOCK_GETH_MESSAGE, deps.EvmKeeper.GetEVMConfig(deps.Ctx), evm.NewNoOpTracer(), stateDB) + return evmObj, stateDB +} + +func (deps *TestDeps) GethSigner() gethcore.Signer { + return gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) +} + +func (deps TestDeps) GoCtx() context.Context { + return sdk.WrapSDKContext(deps.Ctx) +} diff --git a/x/evm/evmtest/tx.go b/x/evm/evmtest/tx.go new file mode 100644 index 000000000..a5803a3f7 --- /dev/null +++ b/x/evm/evmtest/tx.go @@ -0,0 +1,374 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmtest + +import ( + "encoding/json" + "fmt" + "math/big" + "testing" + + "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + gethparams "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" + + srvconfig "github.com/NibiruChain/nibiru/v2/app/server/config" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" +) + +// ExecuteNibiTransfer executes nibi transfer +func ExecuteNibiTransfer(deps *TestDeps, t *testing.T) (*evm.MsgEthereumTx, *evm.MsgEthereumTxResponse) { + nonce := deps.NewStateDB().GetNonce(deps.Sender.EthAddr) + recipient := NewEthPrivAcc().EthAddr + + txArgs := evm.JsonTxArgs{ + From: &deps.Sender.EthAddr, + To: &recipient, + Nonce: (*hexutil.Uint64)(&nonce), + } + ethTxMsg, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner(txArgs, deps, deps.Sender) + require.NoError(t, err) + err = ethTxMsg.Sign(gethSigner, krSigner) + require.NoError(t, err) + + resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) + require.NoError(t, err) + require.Empty(t, resp.VmError) + return ethTxMsg, resp +} + +type DeployContractResult struct { + TxResp *evm.MsgEthereumTxResponse + EthTxMsg *evm.MsgEthereumTx + ContractData embeds.CompiledEvmContract + Nonce uint64 + ContractAddr gethcommon.Address +} + +func DeployContract( + deps *TestDeps, + contract embeds.CompiledEvmContract, + args ...any, +) (result *DeployContractResult, err error) { + // Use contract args + packedArgs, err := contract.ABI.Pack("", args...) + if err != nil { + return nil, errors.Wrap(err, "failed to pack contract args") + } + bytecodeForCall := append(contract.Bytecode, packedArgs...) + + nonce := deps.EvmKeeper.GetAccNonce(deps.Ctx, deps.Sender.EthAddr) + ethTxMsg, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner( + evm.JsonTxArgs{ + Nonce: (*hexutil.Uint64)(&nonce), + Input: (*hexutil.Bytes)(&bytecodeForCall), + From: &deps.Sender.EthAddr, + }, deps, deps.Sender, + ) + if err != nil { + return nil, errors.Wrap(err, "failed to generate and sign eth tx msg") + } + if err := ethTxMsg.Sign(gethSigner, krSigner); err != nil { + return nil, errors.Wrap(err, "failed to generate and sign eth tx msg") + } + + resp, err := deps.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) + if err != nil { + return nil, errors.Wrap(err, "failed to execute ethereum tx") + } + if resp.VmError != "" { + return nil, fmt.Errorf("vm error: %s", resp.VmError) + } + + return &DeployContractResult{ + TxResp: resp, + EthTxMsg: ethTxMsg, + ContractData: contract, + Nonce: nonce, + ContractAddr: crypto.CreateAddress(deps.Sender.EthAddr, nonce), + }, nil +} + +// DeployAndExecuteERC20Transfer deploys contract, executes transfer and returns tx hash +func DeployAndExecuteERC20Transfer( + deps *TestDeps, t *testing.T, +) ( + erc20Transfer *evm.MsgEthereumTx, + predecessors []*evm.MsgEthereumTx, + contractAddr gethcommon.Address, +) { + // TX 1: Deploy ERC-20 contract + deployResp, err := DeployContract(deps, embeds.SmartContract_TestERC20) + require.NoError(t, err) + contractData := deployResp.ContractData + nonce := deployResp.Nonce + + // Contract address is deterministic + contractAddr = crypto.CreateAddress(deps.Sender.EthAddr, nonce) + deps.App.Commit() + predecessors = []*evm.MsgEthereumTx{ + deployResp.EthTxMsg, + } + + // TX 2: execute ERC-20 contract transfer + input, err := contractData.ABI.Pack( + "transfer", NewEthPrivAcc().EthAddr, new(big.Int).SetUint64(1000), + ) + require.NoError(t, err) + nonce = deps.NewStateDB().GetNonce(deps.Sender.EthAddr) + txArgs := evm.JsonTxArgs{ + From: &deps.Sender.EthAddr, + To: &contractAddr, + Nonce: (*hexutil.Uint64)(&nonce), + Data: (*hexutil.Bytes)(&input), + } + erc20Transfer, gethSigner, krSigner, err := GenerateEthTxMsgAndSigner(txArgs, deps, deps.Sender) + require.NoError(t, err) + err = erc20Transfer.Sign(gethSigner, krSigner) + require.NoError(t, err) + + resp, err := deps.App.EvmKeeper.EthereumTx(deps.GoCtx(), erc20Transfer) + require.NoError(t, err) + require.Empty(t, resp.VmError) + + return erc20Transfer, predecessors, contractAddr +} + +var DefaultEthCallGasLimit = srvconfig.DefaultEthCallGasLimit + +// GenerateEthTxMsgAndSigner estimates gas, sets gas limit and returns signer for +// the tx. +// +// Usage: +// +// ```go +// evmTxMsg, gethSigner, krSigner, _ := GenerateEthTxMsgAndSigner( +// jsonTxArgs, &deps, sender, +// ) +// err := evmTxMsg.Sign(gethSigner, sender.KeyringSigner) +// ``` +func GenerateEthTxMsgAndSigner( + jsonTxArgs evm.JsonTxArgs, deps *TestDeps, sender EthPrivKeyAcc, +) (evmTxMsg *evm.MsgEthereumTx, gethSigner gethcore.Signer, krSigner keyring.Signer, err error) { + estimateArgs, err := json.Marshal(&jsonTxArgs) + if err != nil { + return + } + res, err := deps.App.EvmKeeper.EstimateGas( + sdk.WrapSDKContext(deps.Ctx), + &evm.EthCallRequest{ + Args: estimateArgs, + GasCap: srvconfig.DefaultEthCallGasLimit, + ProposerAddress: []byte{}, + ChainId: deps.App.EvmKeeper.EthChainID(deps.Ctx).Int64(), + }, + ) + if err != nil { + return + } + jsonTxArgs.Gas = (*hexutil.Uint64)(&res.Gas) + + evmTxMsg = jsonTxArgs.ToMsgEthTx() + gethSigner = gethcore.LatestSignerForChainID(deps.App.EvmKeeper.EthChainID(deps.Ctx)) + return evmTxMsg, gethSigner, sender.KeyringSigner, nil +} + +type TxTransferWei struct { + Deps *TestDeps + To gethcommon.Address + AmountWei *big.Int + GasLimit uint64 +} + +func (tx TxTransferWei) Build() (evmTxMsg *evm.MsgEthereumTx, err error) { + gasLimit := tx.GasLimit + if tx.GasLimit == 0 { + gasLimit = gethparams.TxGas + } + deps, to, amountWei := tx.Deps, tx.To, tx.AmountWei + + ethAcc := deps.Sender + var innerTxData []byte = nil + var accessList gethcore.AccessList = nil + evmTxMsg, err = NewEthTxMsgFromTxData( + deps, + gethcore.LegacyTxType, + innerTxData, + deps.EvmKeeper.GetAccNonce(deps.Ctx, ethAcc.EthAddr), + &to, + amountWei, + gasLimit, + accessList, + ) + if err != nil { + err = fmt.Errorf("error building tx: %w", err) + } + return +} + +func (tx TxTransferWei) Run() (evmResp *evm.MsgEthereumTxResponse, err error) { + deps := tx.Deps + evmTxMsg, err := tx.Build() + if err != nil { + return + } + evmResp, err = deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), evmTxMsg) + if err != nil { + err = fmt.Errorf("error while transferring wei: %w", err) + } + return evmResp, err +} + +// -------------------------------------------------- +// Templates +// -------------------------------------------------- + +// ValidLegacyTx: Useful initial condition for tests +// Exported only for use in tests. +func ValidLegacyTx() *evm.LegacyTx { + sdkInt := sdkmath.NewIntFromBigInt(evm.NativeToWei(big.NewInt(420))) + return &evm.LegacyTx{ + Nonce: 24, + GasLimit: 50_000, + To: gethcommon.HexToAddress("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed").Hex(), + GasPrice: &sdkInt, + Amount: &sdkInt, + Data: []byte{}, + V: []byte{}, + R: []byte{}, + S: []byte{}, + } +} + +// GethTxType represents different Ethereum transaction types as defined in +// go-ethereum, such as Legacy, AccessList, and DynamicFee transactions. +type GethTxType = uint8 + +func TxTemplateAccessListTx() *gethcore.AccessListTx { + return &gethcore.AccessListTx{ + GasPrice: big.NewInt(1), + Gas: gethparams.TxGas, + To: &gethcommon.Address{}, + Value: big.NewInt(0), + Data: []byte{}, + } +} + +func TxTemplateLegacyTx() *gethcore.LegacyTx { + return &gethcore.LegacyTx{ + GasPrice: big.NewInt(1), + Gas: gethparams.TxGas, + To: &gethcommon.Address{}, + Value: big.NewInt(0), + Data: []byte{}, + } +} + +func TxTemplateDynamicFeeTx() *gethcore.DynamicFeeTx { + return &gethcore.DynamicFeeTx{ + GasFeeCap: big.NewInt(10), + GasTipCap: big.NewInt(2), + Gas: gethparams.TxGas, + To: &gethcommon.Address{}, + Value: big.NewInt(0), + Data: []byte{}, + } +} + +// NewEthTxMsgFromTxData creates an Ethereum transaction message based on +// the specified txType (Legacy, AccessList, or DynamicFee). This function +// populates transaction fields like nonce, recipient, value, and gas, with +// an optional access list for AccessList and DynamicFee types. The transaction +// is signed using the provided dependencies. +// +// Parameters: +// - deps: Required dependencies including the sender address and signer. +// - txType: Transaction type (Legacy, AccessList, or DynamicFee). +// - innerTxData: Byte slice of transaction data (input). +// - nonce: Transaction nonce. +// - to: Recipient address. +// - value: ETH value (in wei) to transfer. +// - gas: Gas limit for the transaction. +// - accessList: Access list for AccessList and DynamicFee types. +// +// Returns: +// - *evm.MsgEthereumTx: Ethereum transaction message ready for submission. +// - error: Any error encountered during creation or signing. +func NewEthTxMsgFromTxData( + deps *TestDeps, + txType GethTxType, + innerTxData []byte, + nonce uint64, + to *gethcommon.Address, + value *big.Int, + gas uint64, + accessList gethcore.AccessList, +) (*evm.MsgEthereumTx, error) { + if innerTxData == nil { + innerTxData = []byte{} + } + + var ethCoreTx *gethcore.Transaction + switch txType { + case gethcore.LegacyTxType: + innerTx := TxTemplateLegacyTx() + innerTx.Nonce = nonce + innerTx.Data = innerTxData + innerTx.To = to + innerTx.Value = value + innerTx.Gas = gas + ethCoreTx = gethcore.NewTx(innerTx) + case gethcore.AccessListTxType: + innerTx := TxTemplateAccessListTx() + innerTx.Nonce = nonce + innerTx.Data = innerTxData + innerTx.AccessList = accessList + innerTx.To = to + innerTx.Value = value + innerTx.Gas = gas + ethCoreTx = gethcore.NewTx(innerTx) + case gethcore.DynamicFeeTxType: + innerTx := TxTemplateDynamicFeeTx() + innerTx.Nonce = nonce + innerTx.Data = innerTxData + innerTx.To = to + innerTx.Value = value + innerTx.Gas = gas + innerTx.AccessList = accessList + ethCoreTx = gethcore.NewTx(innerTx) + default: + return nil, fmt.Errorf( + "received unknown tx type (%v) in NewEthTxMsgFromTxData", txType) + } + + ethTxMsg := new(evm.MsgEthereumTx) + if err := ethTxMsg.FromEthereumTx(ethCoreTx); err != nil { + return ethTxMsg, err + } + + ethTxMsg.From = deps.Sender.EthAddr.Hex() + return ethTxMsg, ethTxMsg.Sign(deps.GethSigner(), deps.Sender.KeyringSigner) +} + +var MOCK_GETH_MESSAGE = gethcore.NewMessage( + evm.EVM_MODULE_ADDRESS, + nil, + 0, + big.NewInt(0), + 0, + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + []byte{}, + gethcore.AccessList{}, + false, +) diff --git a/x/evm/evmtest/tx_test.go b/x/evm/evmtest/tx_test.go new file mode 100644 index 000000000..63cd7573d --- /dev/null +++ b/x/evm/evmtest/tx_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evmtest_test + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *Suite) TestTransferWei() { + deps := evmtest.NewTestDeps() + + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420))), + )) + + randomAcc := evmtest.NewEthPrivAcc() + to := randomAcc.EthAddr + evmResp, err := evmtest.TxTransferWei{ + Deps: &deps, + To: to, + AmountWei: evm.NativeToWei(big.NewInt(420)), + }.Run() + s.Require().NoErrorf(err, "%#v", evmResp) + s.False(evmResp.Failed(), "%#v", evmResp) + + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, evm.EVMBankDenom, deps.Sender.EthAddr, big.NewInt(69_000), "expect nonzero balance", + ) + + s.Run("DeployAndExecuteERC20Transfer", func() { + evmtest.DeployAndExecuteERC20Transfer(&deps, s.T()) + }) +} diff --git a/x/evm/genesis.go b/x/evm/genesis.go new file mode 100644 index 000000000..15a9d72de --- /dev/null +++ b/x/evm/genesis.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "fmt" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// Validate performs a basic validation of a GenesisAccount fields. +func (ga GenesisAccount) Validate() error { + if err := eth.ValidateAddress(ga.Address); err != nil { + return err + } + return ga.Storage.Validate() +} + +// DefaultGenesisState sets default evm genesis state with empty accounts and default params and +// chain config values. +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + Accounts: []GenesisAccount{}, + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + seenAccounts := make(map[string]bool) + for _, acc := range gs.Accounts { + if seenAccounts[acc.Address] { + return fmt.Errorf("duplicate genesis account %s", acc.Address) + } + if err := acc.Validate(); err != nil { + return fmt.Errorf("invalid genesis account %s: %w", acc.Address, err) + } + seenAccounts[acc.Address] = true + } + + return gs.Params.Validate() +} diff --git a/x/evm/genesis.pb.go b/x/evm/genesis.pb.go new file mode 100644 index 000000000..59fd50d09 --- /dev/null +++ b/x/evm/genesis.pb.go @@ -0,0 +1,745 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: eth/evm/v1/genesis.proto + +package evm + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the evm module's genesis state. +type GenesisState struct { + // accounts is an array containing the ethereum genesis accounts. + Accounts []GenesisAccount `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts"` + // params defines all the parameters of the module. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` + // Fungible token mappings corresponding to ERC-20 smart contract tokens. + FuntokenMappings []FunToken `protobuf:"bytes,3,rep,name=funtoken_mappings,json=funtokenMappings,proto3" json:"funtoken_mappings"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_d41c81841e3983b5, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetAccounts() []GenesisAccount { + if m != nil { + return m.Accounts + } + return nil +} + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func (m *GenesisState) GetFuntokenMappings() []FunToken { + if m != nil { + return m.FuntokenMappings + } + return nil +} + +// GenesisAccount defines an account to be initialized in the genesis state. +// Its main difference between with Geth's GenesisAccount is that it uses a +// custom storage type and that it doesn't contain the private key field. +type GenesisAccount struct { + // address defines an ethereum hex formated address of an account + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // code defines the hex bytes of the account code. + Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"` + // storage defines the set of state key values for the account. + Storage Storage `protobuf:"bytes,3,rep,name=storage,proto3,castrepeated=Storage" json:"storage"` +} + +func (m *GenesisAccount) Reset() { *m = GenesisAccount{} } +func (m *GenesisAccount) String() string { return proto.CompactTextString(m) } +func (*GenesisAccount) ProtoMessage() {} +func (*GenesisAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_d41c81841e3983b5, []int{1} +} +func (m *GenesisAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisAccount.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisAccount.Merge(m, src) +} +func (m *GenesisAccount) XXX_Size() int { + return m.Size() +} +func (m *GenesisAccount) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisAccount proto.InternalMessageInfo + +func (m *GenesisAccount) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *GenesisAccount) GetCode() string { + if m != nil { + return m.Code + } + return "" +} + +func (m *GenesisAccount) GetStorage() Storage { + if m != nil { + return m.Storage + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "eth.evm.v1.GenesisState") + proto.RegisterType((*GenesisAccount)(nil), "eth.evm.v1.GenesisAccount") +} + +func init() { proto.RegisterFile("eth/evm/v1/genesis.proto", fileDescriptor_d41c81841e3983b5) } + +var fileDescriptor_d41c81841e3983b5 = []byte{ + // 336 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x91, 0x31, 0x4e, 0xc3, 0x30, + 0x14, 0x86, 0x63, 0x5a, 0xb5, 0xd4, 0x45, 0x40, 0xad, 0x0e, 0x51, 0x87, 0xb4, 0xea, 0x80, 0x3a, + 0xc5, 0xb4, 0xac, 0x1d, 0xa0, 0x48, 0x74, 0x02, 0xa1, 0x94, 0x89, 0x05, 0xb9, 0xa9, 0x49, 0x22, + 0x14, 0x3b, 0x8a, 0x9d, 0x88, 0x81, 0x43, 0x70, 0x0e, 0x2e, 0x42, 0xc7, 0x8e, 0x4c, 0x80, 0xda, + 0x8b, 0x20, 0x3b, 0x0e, 0x84, 0xed, 0xf7, 0xf3, 0xff, 0xbd, 0xff, 0xd9, 0x0f, 0xda, 0x54, 0x86, + 0x98, 0xe6, 0x31, 0xce, 0xc7, 0x38, 0xa0, 0x8c, 0x8a, 0x48, 0xb8, 0x49, 0xca, 0x25, 0x47, 0x90, + 0xca, 0xd0, 0xa5, 0x79, 0xec, 0xe6, 0xe3, 0x5e, 0xb7, 0xe2, 0x52, 0x25, 0xed, 0xe8, 0x75, 0x03, + 0x1e, 0x70, 0x2d, 0xb1, 0x52, 0x45, 0x75, 0xf8, 0x0e, 0xe0, 0xc1, 0xbc, 0xe8, 0xb4, 0x90, 0x44, + 0x52, 0x34, 0x85, 0xfb, 0xc4, 0xf7, 0x79, 0xc6, 0xa4, 0xb0, 0xc1, 0xa0, 0x36, 0x6a, 0x4f, 0x7a, + 0xee, 0x5f, 0x6f, 0xd7, 0x78, 0x2f, 0x0a, 0xcb, 0xac, 0xbe, 0xfe, 0xec, 0x5b, 0xde, 0x2f, 0x81, + 0x4e, 0x61, 0x23, 0x21, 0x29, 0x89, 0x85, 0xbd, 0x37, 0x00, 0xa3, 0xf6, 0x04, 0x55, 0xd9, 0x5b, + 0x7d, 0x63, 0x18, 0xe3, 0x43, 0x73, 0xd8, 0x79, 0xcc, 0x98, 0xe4, 0x4f, 0x94, 0x3d, 0xc4, 0x24, + 0x49, 0x22, 0x16, 0x08, 0xbb, 0xa6, 0x83, 0xbb, 0x55, 0xf8, 0x2a, 0x63, 0x77, 0xca, 0x64, 0xf0, + 0xe3, 0x12, 0xba, 0x36, 0xcc, 0xf0, 0x05, 0x1e, 0xfe, 0x1f, 0x0e, 0xd9, 0xb0, 0x49, 0x56, 0xab, + 0x94, 0x0a, 0xf5, 0x12, 0x30, 0x6a, 0x79, 0xe5, 0x11, 0x21, 0x58, 0xf7, 0xf9, 0x8a, 0xea, 0x21, + 0x5b, 0x9e, 0xd6, 0x68, 0x0a, 0x9b, 0x42, 0xf2, 0x94, 0x04, 0xd4, 0xc4, 0x77, 0xaa, 0xf1, 0xfa, + 0x73, 0x66, 0x47, 0x2a, 0xfb, 0xed, 0xab, 0xdf, 0x5c, 0x14, 0x4e, 0xaf, 0x44, 0x66, 0xe7, 0xeb, + 0xad, 0x03, 0x36, 0x5b, 0x07, 0x7c, 0x6f, 0x1d, 0xf0, 0xba, 0x73, 0xac, 0xcd, 0xce, 0xb1, 0x3e, + 0x76, 0x8e, 0x75, 0x7f, 0x12, 0x44, 0x32, 0xcc, 0x96, 0xae, 0xcf, 0x63, 0x7c, 0x13, 0x2d, 0xa3, + 0x34, 0xbb, 0x0c, 0x49, 0xc4, 0x30, 0xd3, 0x1a, 0xe7, 0x13, 0xfc, 0xac, 0xb6, 0xb4, 0x6c, 0xe8, + 0x85, 0x9c, 0xfd, 0x04, 0x00, 0x00, 0xff, 0xff, 0x72, 0xb9, 0x50, 0x40, 0xe4, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FuntokenMappings) > 0 { + for iNdEx := len(m.FuntokenMappings) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FuntokenMappings[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Accounts) > 0 { + for iNdEx := len(m.Accounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Accounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GenesisAccount) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Storage) > 0 { + for iNdEx := len(m.Storage) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Storage[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Code) > 0 { + i -= len(m.Code) + copy(dAtA[i:], m.Code) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Code))) + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Accounts) > 0 { + for _, e := range m.Accounts { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + if len(m.FuntokenMappings) > 0 { + for _, e := range m.FuntokenMappings { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *GenesisAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = len(m.Code) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + if len(m.Storage) > 0 { + for _, e := range m.Storage { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Accounts", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Accounts = append(m.Accounts, GenesisAccount{}) + if err := m.Accounts[len(m.Accounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FuntokenMappings", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FuntokenMappings = append(m.FuntokenMappings, FunToken{}) + if err := m.FuntokenMappings[len(m.FuntokenMappings)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GenesisAccount) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Code = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Storage", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Storage = append(m.Storage, State{}) + if err := m.Storage[len(m.Storage)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/evm/genesis_test.go b/x/evm/genesis_test.go new file mode 100644 index 000000000..46c30f2d2 --- /dev/null +++ b/x/evm/genesis_test.go @@ -0,0 +1,195 @@ +package evm_test + +import ( + "encoding/json" + "testing" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +type GenesisSuite struct { + suite.Suite + + address string + hash gethcommon.Hash + code string +} + +func (s *GenesisSuite) SetupTest() { + priv, err := ethsecp256k1.GenerateKey() + s.Require().NoError(err) + + s.address = gethcommon.BytesToAddress(priv.PubKey().Address().Bytes()).String() + s.hash = gethcommon.BytesToHash([]byte("hash")) + s.code = gethcommon.Bytes2Hex([]byte{1, 2, 3}) +} + +func TestGenesisSuite(t *testing.T) { + suite.Run(t, new(GenesisSuite)) +} + +func (s *GenesisSuite) TestValidateGenesisAccount() { + testCases := []struct { + name string + genAcc evm.GenesisAccount + expPass bool + }{ + { + name: "valid genesis account", + genAcc: evm.GenesisAccount{ + Address: s.address, + Code: s.code, + Storage: evm.Storage{ + evm.NewStateFromEthHashes(s.hash, s.hash), + }, + }, + expPass: true, + }, + { + name: "empty account address bytes", + genAcc: evm.GenesisAccount{ + Address: "", + Code: s.code, + Storage: evm.Storage{ + evm.NewStateFromEthHashes(s.hash, s.hash), + }, + }, + expPass: false, + }, + { + name: "empty code bytes", + genAcc: evm.GenesisAccount{ + Address: s.address, + Code: "", + Storage: evm.Storage{ + evm.NewStateFromEthHashes(s.hash, s.hash), + }, + }, + expPass: true, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genAcc.Validate() + if tc.expPass { + s.Require().NoError(err, tc.name) + } else { + s.Require().Error(err, tc.name) + } + } +} + +func (s *GenesisSuite) TestValidateGenesis() { + testCases := []struct { + name string + genState *evm.GenesisState + wantErr string + }{ + { + name: "default", + genState: evm.DefaultGenesisState(), + }, + { + name: "valid genesis", + genState: &evm.GenesisState{ + Accounts: []evm.GenesisAccount{ + { + Address: s.address, + + Code: s.code, + Storage: evm.Storage{ + {Key: s.hash.String()}, + }, + }, + }, + Params: evm.DefaultParams(), + }, + }, + { + name: "empty genesis", + genState: &evm.GenesisState{}, + }, + { + name: "copied genesis", + genState: &evm.GenesisState{ + Accounts: evm.DefaultGenesisState().Accounts, + Params: evm.DefaultGenesisState().Params, + }, + }, + { + name: "happy genesis with account", + genState: &evm.GenesisState{ + Accounts: []evm.GenesisAccount{ + { + Address: gethcommon.Address{}.String(), // zero address + }, + }, + }, + }, + { + name: "invalid genesis account", + genState: &evm.GenesisState{ + Accounts: []evm.GenesisAccount{ + { + Address: "123456", // not a valid ethereum hex address + + Code: s.code, + Storage: evm.Storage{ + {Key: s.hash.String()}, + }, + }, + }, + Params: evm.DefaultParams(), + }, + wantErr: "not a valid ethereum hex address", + }, + { + name: "duplicate account", + wantErr: "duplicate genesis account", + genState: &evm.GenesisState{ + Accounts: []evm.GenesisAccount{ + { + Address: s.address, + + Code: s.code, + Storage: evm.Storage{ + {Key: s.hash.String()}, + }, + }, + { + Address: s.address, + + Code: s.code, + Storage: evm.Storage{ + {Key: s.hash.String()}, + }, + }, + }, + }, + }, + { + name: "happy: empty params", + genState: &evm.GenesisState{ + Params: evm.Params{}, + }, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + err := tc.genState.Validate() + jsonBz, _ := json.Marshal(tc.genState) + jsonStr := string(jsonBz) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr, jsonStr) + return + } + s.Require().NoError(err, jsonStr) + }) + } +} diff --git a/x/evm/json_tx_args.go b/x/evm/json_tx_args.go new file mode 100644 index 000000000..2318250a7 --- /dev/null +++ b/x/evm/json_tx_args.go @@ -0,0 +1,252 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "errors" + "fmt" + "math/big" + + sdkmath "cosmossdk.io/math" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + geth "github.com/ethereum/go-ethereum/core/types" +) + +// JsonTxArgs represents the arguments to construct a new transaction +// or a message call using JSON-RPC. +// Duplicate struct definition since geth struct is in internal package +// Ref: https://github.com/ethereum/go-ethereum/blob/release/1.10.4/internal/ethapi/transaction_args.go#L36 +type JsonTxArgs struct { + From *common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` + MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` + Value *hexutil.Big `json:"value"` + Nonce *hexutil.Uint64 `json:"nonce"` + + // We accept "data" and "input" for backwards-compatibility reasons. + // "input" is the newer name and should be preferred by clients. + // Issue detail: https://github.com/ethereum/go-ethereum/issues/15628 + Data *hexutil.Bytes `json:"data"` + // Both "data" and "input" are accepted for backwards-compatibility reasons. + // "input" is the newer name and should be preferred by clients. + // Issue detail: https://github.com/ethereum/go-ethereum/issues/15628 + Input *hexutil.Bytes `json:"input"` + + // Introduced by AccessListTxType transaction. + AccessList *geth.AccessList `json:"accessList,omitempty"` + ChainID *hexutil.Big `json:"chainId,omitempty"` +} + +// String return the struct in a string format +func (args *JsonTxArgs) String() string { + // Todo: There is currently a bug with hexutil.Big when the value its nil, printing would trigger an exception + return fmt.Sprintf("TransactionArgs{From:%v, To:%v, Gas:%v,"+ + " Nonce:%v, Data:%v, Input:%v, AccessList:%v}", + args.From, + args.To, + args.Gas, + args.Nonce, + args.Data, + args.Input, + args.AccessList) +} + +// ToMsgEthTx converts the arguments to an ethereum transaction. +// This assumes that setTxDefaults has been called. +func (args *JsonTxArgs) ToMsgEthTx() *MsgEthereumTx { + var ( + chainID, value, gasPrice, maxFeePerGas, maxPriorityFeePerGas sdkmath.Int + gas, nonce uint64 + from, to string + ) + + // Set sender address or use zero address if none specified. + if args.ChainID != nil { + chainID = sdkmath.NewIntFromBigInt(args.ChainID.ToInt()) + } + + if args.Nonce != nil { + nonce = uint64(*args.Nonce) + } + + if args.Gas != nil { + gas = uint64(*args.Gas) + } + + if args.GasPrice != nil { + gasPrice = sdkmath.NewIntFromBigInt(args.GasPrice.ToInt()) + } + + if args.MaxFeePerGas != nil { + maxFeePerGas = sdkmath.NewIntFromBigInt(args.MaxFeePerGas.ToInt()) + } + + if args.MaxPriorityFeePerGas != nil { + maxPriorityFeePerGas = sdkmath.NewIntFromBigInt(args.MaxPriorityFeePerGas.ToInt()) + } + + if args.Value != nil { + value = sdkmath.NewIntFromBigInt(args.Value.ToInt()) + } + + if args.To != nil { + to = args.To.Hex() + } + + var data TxData + switch { + case args.MaxFeePerGas != nil: + al := AccessList{} + if args.AccessList != nil { + al = NewAccessList(args.AccessList) + } + + data = &DynamicFeeTx{ + To: to, + ChainID: &chainID, + Nonce: nonce, + GasLimit: gas, + GasFeeCap: &maxFeePerGas, + GasTipCap: &maxPriorityFeePerGas, + Amount: &value, + Data: args.GetData(), + Accesses: al, + } + case args.AccessList != nil: + data = &AccessListTx{ + To: to, + ChainID: &chainID, + Nonce: nonce, + GasLimit: gas, + GasPrice: &gasPrice, + Amount: &value, + Data: args.GetData(), + Accesses: NewAccessList(args.AccessList), + } + default: + data = &LegacyTx{ + To: to, + Nonce: nonce, + GasLimit: gas, + GasPrice: &gasPrice, + Amount: &value, + Data: args.GetData(), + } + } + + anyData, err := PackTxData(data) + if err != nil { + return nil + } + + if args.From != nil { + from = args.From.Hex() + } + + msg := MsgEthereumTx{ + Data: anyData, + From: from, + } + msg.Hash = msg.AsTransaction().Hash().Hex() + return &msg +} + +// ToMessage converts the arguments to the Message type used by the core evm. +// This assumes that setTxDefaults has been called. +func (args *JsonTxArgs) ToMessage(globalGasCap uint64, baseFeeWei *big.Int) (geth.Message, error) { + // Reject invalid combinations of pre- and post-1559 fee styles + if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) { + return geth.Message{}, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + } + + // Set sender address or use zero address if none specified. + addr := args.GetFrom() + + // Set default gas & gas price if none were set + gas := globalGasCap + if gas == 0 { + gas = uint64(math.MaxUint64 / 2) + } + if args.Gas != nil { + gas = uint64(*args.Gas) + } + if globalGasCap != 0 && globalGasCap < gas { + gas = globalGasCap + } + var ( + gasPrice *big.Int + gasFeeCap *big.Int + gasTipCap *big.Int + ) + if baseFeeWei == nil { + // If there's no basefee, then it must be a non-1559 execution + gasPrice = new(big.Int) + if args.GasPrice != nil { + gasPrice = args.GasPrice.ToInt() + } + gasFeeCap, gasTipCap = gasPrice, gasPrice + } else { + // A basefee is provided, necessitating 1559-type execution + if args.GasPrice != nil { + // User specified the legacy gas field, convert to 1559 gas typing + gasPrice = args.GasPrice.ToInt() + gasFeeCap, gasTipCap = gasPrice, gasPrice + } else { + // User specified 1559 gas feilds (or none), use those + gasFeeCap = new(big.Int) + if args.MaxFeePerGas != nil { + gasFeeCap = args.MaxFeePerGas.ToInt() + } + gasTipCap = new(big.Int) + if args.MaxPriorityFeePerGas != nil { + gasTipCap = args.MaxPriorityFeePerGas.ToInt() + } + // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes + gasPrice = new(big.Int) + if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 { + gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFeeWei), gasFeeCap) + } + } + } + value := new(big.Int) + if args.Value != nil { + value = args.Value.ToInt() + } + data := args.GetData() + var accessList geth.AccessList + if args.AccessList != nil { + accessList = *args.AccessList + } + + nonce := uint64(0) + if args.Nonce != nil { + nonce = uint64(*args.Nonce) + } + + msg := geth.NewMessage(addr, args.To, nonce, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, true) + return msg, nil +} + +// GetFrom retrieves the transaction sender address. +func (args *JsonTxArgs) GetFrom() common.Address { + if args.From == nil { + return common.Address{} + } + return *args.From +} + +// GetData retrieves the transaction calldata. Input field is preferred. +func (args *JsonTxArgs) GetData() []byte { + if args.Input != nil { + return *args.Input + } + if args.Data != nil { + return *args.Data + } + return nil +} diff --git a/x/evm/json_tx_args_test.go b/x/evm/json_tx_args_test.go new file mode 100644 index 000000000..3a99a8f06 --- /dev/null +++ b/x/evm/json_tx_args_test.go @@ -0,0 +1,290 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm_test + +import ( + "fmt" + "math/big" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethcoretypes "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +func (suite *Suite) TestTxArgsString() { + testCases := []struct { + name string + txArgs evm.JsonTxArgs + expectedString string + }{ + { + "empty tx args", + evm.JsonTxArgs{}, + "TransactionArgs{From:, To:, Gas:, Nonce:, Data:, Input:, AccessList:}", + }, + { + "tx args with fields", + evm.JsonTxArgs{ + From: &suite.addr, + To: &suite.addr, + Gas: &suite.hexUint64, + Nonce: &suite.hexUint64, + Input: &suite.hexInputBytes, + Data: &suite.hexDataBytes, + AccessList: ðcoretypes.AccessList{}, + }, + fmt.Sprintf("TransactionArgs{From:%v, To:%v, Gas:%v, Nonce:%v, Data:%v, Input:%v, AccessList:%v}", + &suite.addr, + &suite.addr, + &suite.hexUint64, + &suite.hexUint64, + &suite.hexDataBytes, + &suite.hexInputBytes, + ðcoretypes.AccessList{}), + }, + } + for _, tc := range testCases { + outputString := tc.txArgs.String() + suite.Require().Equal(outputString, tc.expectedString) + } +} + +func (suite *Suite) TestConvertTxArgsEthTx() { + testCases := []struct { + name string + txArgs evm.JsonTxArgs + }{ + { + "empty tx args", + evm.JsonTxArgs{}, + }, + { + "no nil args", + evm.JsonTxArgs{ + From: &suite.addr, + To: &suite.addr, + Gas: &suite.hexUint64, + GasPrice: &suite.hexBigInt, + MaxFeePerGas: &suite.hexBigInt, + MaxPriorityFeePerGas: &suite.hexBigInt, + Value: &suite.hexBigInt, + Nonce: &suite.hexUint64, + Data: &suite.hexDataBytes, + Input: &suite.hexInputBytes, + AccessList: ðcoretypes.AccessList{{Address: suite.addr, StorageKeys: []ethcommon.Hash{{0}}}}, + ChainID: &suite.hexBigInt, + }, + }, + { + "max fee per gas nil, but access list not nil", + evm.JsonTxArgs{ + From: &suite.addr, + To: &suite.addr, + Gas: &suite.hexUint64, + GasPrice: &suite.hexBigInt, + MaxFeePerGas: nil, + MaxPriorityFeePerGas: &suite.hexBigInt, + Value: &suite.hexBigInt, + Nonce: &suite.hexUint64, + Data: &suite.hexDataBytes, + Input: &suite.hexInputBytes, + AccessList: ðcoretypes.AccessList{{Address: suite.addr, StorageKeys: []ethcommon.Hash{{0}}}}, + ChainID: &suite.hexBigInt, + }, + }, + } + for _, tc := range testCases { + res := tc.txArgs.ToMsgEthTx() + suite.Require().NotNil(res) + } +} + +func (suite *Suite) TestToMessageEVM() { + testCases := []struct { + name string + txArgs evm.JsonTxArgs + globalGasCap uint64 + baseFeeWei *big.Int + expError bool + }{ + { + "empty tx args", + evm.JsonTxArgs{}, + uint64(0), + nil, + false, + }, + { + "specify gasPrice and (maxFeePerGas or maxPriorityFeePerGas)", + evm.JsonTxArgs{ + From: &suite.addr, + To: &suite.addr, + Gas: &suite.hexUint64, + GasPrice: &suite.hexBigInt, + MaxFeePerGas: &suite.hexBigInt, + MaxPriorityFeePerGas: &suite.hexBigInt, + Value: &suite.hexBigInt, + Nonce: &suite.hexUint64, + Data: &suite.hexDataBytes, + Input: &suite.hexInputBytes, + AccessList: ðcoretypes.AccessList{{Address: suite.addr, StorageKeys: []ethcommon.Hash{{0}}}}, + ChainID: &suite.hexBigInt, + }, + uint64(0), + nil, + true, + }, + { + "non-1559 execution, zero gas cap", + evm.JsonTxArgs{ + From: &suite.addr, + To: &suite.addr, + Gas: &suite.hexUint64, + GasPrice: &suite.hexBigInt, + MaxFeePerGas: nil, + MaxPriorityFeePerGas: nil, + Value: &suite.hexBigInt, + Nonce: &suite.hexUint64, + Data: &suite.hexDataBytes, + Input: &suite.hexInputBytes, + AccessList: ðcoretypes.AccessList{{Address: suite.addr, StorageKeys: []ethcommon.Hash{{0}}}}, + ChainID: &suite.hexBigInt, + }, + uint64(0), + nil, + false, + }, + { + "non-1559 execution, nonzero gas cap", + evm.JsonTxArgs{ + From: &suite.addr, + To: &suite.addr, + Gas: &suite.hexUint64, + GasPrice: &suite.hexBigInt, + MaxFeePerGas: nil, + MaxPriorityFeePerGas: nil, + Value: &suite.hexBigInt, + Nonce: &suite.hexUint64, + Data: &suite.hexDataBytes, + Input: &suite.hexInputBytes, + AccessList: ðcoretypes.AccessList{{Address: suite.addr, StorageKeys: []ethcommon.Hash{{0}}}}, + ChainID: &suite.hexBigInt, + }, + uint64(1), + nil, + false, + }, + { + "1559-type execution, nil gas price", + evm.JsonTxArgs{ + From: &suite.addr, + To: &suite.addr, + Gas: &suite.hexUint64, + GasPrice: nil, + MaxFeePerGas: &suite.hexBigInt, + MaxPriorityFeePerGas: &suite.hexBigInt, + Value: &suite.hexBigInt, + Nonce: &suite.hexUint64, + Data: &suite.hexDataBytes, + Input: &suite.hexInputBytes, + AccessList: ðcoretypes.AccessList{{Address: suite.addr, StorageKeys: []ethcommon.Hash{{0}}}}, + ChainID: &suite.hexBigInt, + }, + uint64(1), + suite.bigInt, + false, + }, + { + "1559-type execution, non-nil gas price", + evm.JsonTxArgs{ + From: &suite.addr, + To: &suite.addr, + Gas: &suite.hexUint64, + GasPrice: &suite.hexBigInt, + MaxFeePerGas: nil, + MaxPriorityFeePerGas: nil, + Value: &suite.hexBigInt, + Nonce: &suite.hexUint64, + Data: &suite.hexDataBytes, + Input: &suite.hexInputBytes, + AccessList: ðcoretypes.AccessList{{Address: suite.addr, StorageKeys: []ethcommon.Hash{{0}}}}, + ChainID: &suite.hexBigInt, + }, + uint64(1), + suite.bigInt, + false, + }, + } + for _, tc := range testCases { + res, err := tc.txArgs.ToMessage(tc.globalGasCap, tc.baseFeeWei) + + if tc.expError { + suite.Require().NotNil(err) + } else { + suite.Require().Nil(err) + suite.Require().NotNil(res) + } + } +} + +func (suite *Suite) TestGetFrom() { + testCases := []struct { + name string + txArgs evm.JsonTxArgs + expAddress ethcommon.Address + }{ + { + "empty from field", + evm.JsonTxArgs{}, + ethcommon.Address{}, + }, + { + "non-empty from field", + evm.JsonTxArgs{ + From: &suite.addr, + }, + suite.addr, + }, + } + for _, tc := range testCases { + retrievedAddress := tc.txArgs.GetFrom() + suite.Require().Equal(retrievedAddress, tc.expAddress) + } +} + +func (suite *Suite) TestGetData() { + testCases := []struct { + name string + txArgs evm.JsonTxArgs + expectedOutput []byte + }{ + { + "empty input and data fields", + evm.JsonTxArgs{ + Data: nil, + Input: nil, + }, + nil, + }, + { + "empty input field, non-empty data field", + evm.JsonTxArgs{ + Data: &suite.hexDataBytes, + Input: nil, + }, + []byte("data"), + }, + { + "non-empty input and data fields", + evm.JsonTxArgs{ + Data: &suite.hexDataBytes, + Input: &suite.hexInputBytes, + }, + []byte("input"), + }, + } + for _, tc := range testCases { + retrievedData := tc.txArgs.GetData() + suite.Require().Equal(retrievedData, tc.expectedOutput) + } +} diff --git a/x/evm/keeper/bank_extension.go b/x/evm/keeper/bank_extension.go new file mode 100644 index 000000000..5531b9c5c --- /dev/null +++ b/x/evm/keeper/bank_extension.go @@ -0,0 +1,321 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +var _ bankkeeper.Keeper = &NibiruBankKeeper{} + +type NibiruBankKeeper struct { + bankkeeper.BaseKeeper + StateDB *statedb.StateDB +} + +func (evmKeeper *Keeper) NewStateDB( + ctx sdk.Context, txConfig statedb.TxConfig, +) *statedb.StateDB { + stateDB := statedb.New(ctx, evmKeeper, txConfig) + evmKeeper.Bank.StateDB = stateDB + return stateDB +} + +func (bk NibiruBankKeeper) InputOutputCoins( + ctx sdk.Context, + input []banktypes.Input, + output []banktypes.Output, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + return bk.BaseKeeper.InputOutputCoins(ctx, input, output) + }, + func(ctx sdk.Context) { + for _, input := range input { + if findEtherBalanceChangeFromCoins(input.Coins) { + bk.SyncStateDBWithAccount(ctx, sdk.MustAccAddressFromBech32(input.Address)) + } + } + for _, output := range output { + if findEtherBalanceChangeFromCoins(output.Coins) { + bk.SyncStateDBWithAccount(ctx, sdk.MustAccAddressFromBech32(output.Address)) + } + } + }, + ) +} + +func (bk NibiruBankKeeper) DelegateCoins( + ctx sdk.Context, + delegatorAddr sdk.AccAddress, + moduleBech32Addr sdk.AccAddress, + coins sdk.Coins, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + return bk.BaseKeeper.DelegateCoins(ctx, delegatorAddr, moduleBech32Addr, coins) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(coins) { + bk.SyncStateDBWithAccount(ctx, delegatorAddr) + } + }, + ) +} + +func (bk NibiruBankKeeper) UndelegateCoins( + ctx sdk.Context, + delegatorAddr sdk.AccAddress, + moduleBech32Addr sdk.AccAddress, + coins sdk.Coins, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + return bk.BaseKeeper.UndelegateCoins(ctx, delegatorAddr, moduleBech32Addr, coins) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(coins) { + bk.SyncStateDBWithAccount(ctx, delegatorAddr) + } + }, + ) +} + +func (bk NibiruBankKeeper) DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + return bk.BaseKeeper.DelegateCoinsFromAccountToModule(ctx, senderAddr, recipientModule, amt) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(amt) { + bk.SyncStateDBWithAccount(ctx, senderAddr) + moduleBech32Addr := auth.NewModuleAddress(recipientModule) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + } + }, + ) +} + +func (bk NibiruBankKeeper) UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + return bk.BaseKeeper.UndelegateCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, amt) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(amt) { + moduleBech32Addr := auth.NewModuleAddress(senderModule) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + bk.SyncStateDBWithAccount(ctx, recipientAddr) + } + }, + ) +} + +func (bk NibiruBankKeeper) MintCoins( + ctx sdk.Context, + moduleName string, + coins sdk.Coins, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + // Use the embedded function from [bankkeeper.Keeper] + return bk.BaseKeeper.MintCoins(ctx, moduleName, coins) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(coins) { + moduleBech32Addr := auth.NewModuleAddress(moduleName) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + } + }, + ) +} + +func (bk NibiruBankKeeper) BurnCoins( + ctx sdk.Context, + moduleName string, + coins sdk.Coins, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + // Use the embedded function from [bankkeeper.Keeper] + return bk.BaseKeeper.BurnCoins(ctx, moduleName, coins) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(coins) { + moduleBech32Addr := auth.NewModuleAddress(moduleName) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + } + }, + ) +} + +// Each Send* operation on the [NibiruBankKeeper] can be described as having a +// base operation (BaseOp) where the [bankkeeper.BaseKeeper] executes some +// business logic and an operation that occurs afterward (AfterOp), where we +// post-process and provide automatic alignment with the EVM [statedb.StateDB]. +// +// Each "AfterOp" tends to consume a negligible amount of gas (<2000 gas), while +// a each "BaseOp" is around 27000 for a single coin transfer. +// +// Although each "AfterOp" consumes a negligible amount of gas, that +// amount of gas consumed is nonzero and depends on whether the current bank +// transaction message occurs within an Ethereum tx or not. +// +// Consistent gas consumption independent of status of the EVM StateDB is brought +// about in [ForceGasInvariant] by consuming only the gas used for the BaseOp. +// This makes sure that post-processing for the EVM [statedb.StateDB] will not +// result in nondeterminism. +func (bk NibiruBankKeeper) ForceGasInvariant( + ctx sdk.Context, + BaseOp func(ctx sdk.Context) error, + AfterOp func(ctx sdk.Context), +) error { + // Assign vars for the tx gas meter + gasMeterBefore := ctx.GasMeter() // Tx gas meter MUST be defined + gasConsumedBefore := gasMeterBefore.GasConsumed() + baseOpGasConsumed := uint64(0) + + defer func() { + // NOTE: we have to refund the entire gasMeterBefore because it's modified by AfterOp + // stateDB.getStateObject() reads from state using the local root ctx which affects the gas meter + gasMeterBefore.RefundGas(gasMeterBefore.GasConsumed(), "") + gasMeterBefore.ConsumeGas(gasConsumedBefore+baseOpGasConsumed, "NibiruBankKeeper invariant") + }() + + // We keep the same gas meter type but reset the gas consumed prior to measuring the base op + // We need the same gas meter type because we use a custom FixedGasMeter for oracle votes in the AnteHandler + // In the defer function, we reset the gas meter again and then add the gasConsumedBefore to baseOpGasConsumed, + // so any modifications to the gasMeterBefore after this point will be inconsequential. + ctx.GasMeter().RefundGas(gasConsumedBefore, "reset gas meter before measuring base op") + + err := BaseOp(ctx) + baseOpGasConsumed = ctx.GasMeter().GasConsumed() + if err != nil { + return err + } + + AfterOp(ctx) + return nil +} + +func (bk NibiruBankKeeper) SendCoins( + ctx sdk.Context, + fromAddr sdk.AccAddress, + toAddr sdk.AccAddress, + coins sdk.Coins, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + return bk.BaseKeeper.SendCoins(ctx, fromAddr, toAddr, coins) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(coins) { + bk.SyncStateDBWithAccount(ctx, fromAddr) + bk.SyncStateDBWithAccount(ctx, toAddr) + } + }, + ) +} + +func (bk *NibiruBankKeeper) SyncStateDBWithAccount( + ctx sdk.Context, acc sdk.AccAddress, +) { + // If there's no StateDB set, it means we're not in an EthereumTx. + if bk.StateDB == nil { + return + } + + balanceWei := evm.NativeToWei( + bk.GetBalance(ctx, acc, evm.EVMBankDenom).Amount.BigInt(), + ) + bk.StateDB.SetBalanceWei(eth.NibiruAddrToEthAddr(acc), balanceWei) +} + +func findEtherBalanceChangeFromCoins(coins sdk.Coins) (found bool) { + for _, c := range coins { + if c.Denom == evm.EVMBankDenom { + return true + } + } + return false +} + +func (bk NibiruBankKeeper) SendCoinsFromAccountToModule( + ctx sdk.Context, + senderAddr sdk.AccAddress, + recipientModule string, + coins sdk.Coins, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + // Use the embedded function from [bankkeeper.Keeper] + return bk.BaseKeeper.SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, coins) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(coins) { + bk.SyncStateDBWithAccount(ctx, senderAddr) + moduleBech32Addr := auth.NewModuleAddress(recipientModule) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + } + }, + ) +} + +func (bk NibiruBankKeeper) SendCoinsFromModuleToAccount( + ctx sdk.Context, + senderModule string, + recipientAddr sdk.AccAddress, + coins sdk.Coins, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + // Use the embedded function from [bankkeeper.Keeper] + return bk.BaseKeeper.SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, coins) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(coins) { + moduleBech32Addr := auth.NewModuleAddress(senderModule) + bk.SyncStateDBWithAccount(ctx, moduleBech32Addr) + bk.SyncStateDBWithAccount(ctx, recipientAddr) + } + }, + ) +} + +func (bk NibiruBankKeeper) SendCoinsFromModuleToModule( + ctx sdk.Context, + senderModule string, + recipientModule string, + coins sdk.Coins, +) error { + return bk.ForceGasInvariant( + ctx, + func(ctx sdk.Context) error { + // Use the embedded function from [bankkeeper.Keeper] + return bk.BaseKeeper.SendCoinsFromModuleToModule(ctx, senderModule, recipientModule, coins) + }, + func(ctx sdk.Context) { + if findEtherBalanceChangeFromCoins(coins) { + senderBech32Addr := auth.NewModuleAddress(senderModule) + recipientBech32Addr := auth.NewModuleAddress(recipientModule) + bk.SyncStateDBWithAccount(ctx, senderBech32Addr) + bk.SyncStateDBWithAccount(ctx, recipientBech32Addr) + } + }, + ) +} diff --git a/x/evm/keeper/bank_extension_test.go b/x/evm/keeper/bank_extension_test.go new file mode 100644 index 000000000..7ac29daea --- /dev/null +++ b/x/evm/keeper/bank_extension_test.go @@ -0,0 +1,427 @@ +package keeper_test + +import ( + "encoding/json" + "fmt" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + staking "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/common/hexutil" + gethparams "github.com/ethereum/go-ethereum/params" + "github.com/rs/zerolog/log" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +// TestGasConsumedInvariantSend: The "NibiruBankKeeper" is defined such that +// send operations are meant to have consistent gas consumption regardless of the +// whether the active "StateDB" instance is defined or undefined (nil). This is +// important to prevent consensus failures resulting from nodes reaching an +// inconsistent state after processing the same state transitions and gettng +// conflicting gas results. +func (s *Suite) TestGasConsumedInvariantSend() { + to := evmtest.NewEthPrivAcc() // arbitrary constant + + testCases := []struct { + GasConsumedInvariantScenario + name string + }{ + { + name: "StateDB nil", + GasConsumedInvariantScenario: GasConsumedInvariantScenario{ + BankDenom: evm.EVMBankDenom, + NilStateDB: true, + }, + }, + + { + name: "StateDB defined", + GasConsumedInvariantScenario: GasConsumedInvariantScenario{ + BankDenom: evm.EVMBankDenom, + NilStateDB: false, + }, + }, + + { + name: "StateDB nil, uniba", + GasConsumedInvariantScenario: GasConsumedInvariantScenario{ + BankDenom: "uniba", + NilStateDB: true, + }, + }, + } + + s.T().Log("Check that gas consumption is equal in all scenarios") + var first uint64 + for idx, tc := range testCases { + s.Run(tc.name, func() { + gasConsumed := tc.GasConsumedInvariantScenario.Run(s, to) + s.T().Logf("gasConsumed: %d", gasConsumed) + s.Require().NotZerof(gasConsumed, "gasConsumed should not be zero") + if idx == 0 { + first = gasConsumed + return + } + // Each elem being equal to "first" implies that each elem is equal + s.Equalf( + first, + gasConsumed, + "Gas consumed should be equal", + ) + }) + } +} + +type GasConsumedInvariantScenario struct { + BankDenom string + NilStateDB bool +} + +func (scenario GasConsumedInvariantScenario) Run( + s *Suite, + to evmtest.EthPrivKeyAcc, +) (gasConsumed uint64) { + bankDenom, nilStateDB := scenario.BankDenom, scenario.NilStateDB + deps := evmtest.NewTestDeps() + if nilStateDB { + s.Require().Nil(deps.EvmKeeper.Bank.StateDB) + } else { + deps.NewStateDB() + s.NotNil(deps.EvmKeeper.Bank.StateDB) + } + + sendCoins := sdk.NewCoins(sdk.NewInt64Coin(bankDenom, 420)) + s.NoError( + testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, sendCoins), + ) + + gasConsumedBefore := deps.Ctx.GasMeter().GasConsumed() + s.NoError( + deps.App.BankKeeper.SendCoins( + deps.Ctx, deps.Sender.NibiruAddr, to.NibiruAddr, sendCoins, + ), + ) + gasConsumedAfter := deps.Ctx.GasMeter().GasConsumed() + + s.Greaterf(gasConsumedAfter, gasConsumedBefore, + "gas meter consumed should not be negative: gas consumed after = %d, gas consumed before = %d ", + gasConsumedAfter, gasConsumedBefore, + ) + + return gasConsumedAfter - gasConsumedBefore +} + +func (s *Suite) TestGasConsumedInvariantSendNotNibi() { + to := evmtest.NewEthPrivAcc() // arbitrary constant + + testCases := []struct { + GasConsumedInvariantScenario + name string + }{ + { + name: "StateDB nil A", + GasConsumedInvariantScenario: GasConsumedInvariantScenario{ + BankDenom: "dummy_token_A", + NilStateDB: true, + }, + }, + + { + name: "StateDB defined A", + GasConsumedInvariantScenario: GasConsumedInvariantScenario{ + BankDenom: "dummy_token_A", + NilStateDB: false, + }, + }, + + { + name: "StateDB nil B", + GasConsumedInvariantScenario: GasConsumedInvariantScenario{ + BankDenom: "dummy_token_B", + NilStateDB: true, + }, + }, + + { + name: "StateDB defined B", + GasConsumedInvariantScenario: GasConsumedInvariantScenario{ + BankDenom: "dummy_token_B", + NilStateDB: false, + }, + }, + } + + s.T().Log("Check that gas consumption is equal in all scenarios") + var first uint64 + for idx, tc := range testCases { + s.Run(tc.name, func() { + gasConsumed := tc.GasConsumedInvariantScenario.Run(s, to) + if idx == 0 { + first = gasConsumed + return + } + // Each elem being equal to "first" implies that each elem is equal + s.Equalf( + fmt.Sprintf("%d", first), + fmt.Sprintf("%d", gasConsumed), + "Gas consumed should be equal", + ) + }) + } +} + +type FunctionalGasConsumedInvariantScenario struct { + Setup func(deps *evmtest.TestDeps) + Measure func(deps *evmtest.TestDeps) +} + +func (f FunctionalGasConsumedInvariantScenario) Run(s *Suite) { + var ( + gasConsumedA uint64 // nil StateDB + gasConsumedB uint64 // not nil StateDB + ) + + { + deps := evmtest.NewTestDeps() + s.Nil(deps.EvmKeeper.Bank.StateDB) + + f.Setup(&deps) + + gasConsumedBefore := deps.Ctx.GasMeter().GasConsumed() + f.Measure(&deps) + gasConsumedAfter := deps.Ctx.GasMeter().GasConsumed() + gasConsumedA = gasConsumedAfter - gasConsumedBefore + } + + { + deps := evmtest.NewTestDeps() + deps.NewStateDB() + s.NotNil(deps.EvmKeeper.Bank.StateDB) + + f.Setup(&deps) + + gasConsumedBefore := deps.Ctx.GasMeter().GasConsumed() + f.Measure(&deps) + gasConsumedAfter := deps.Ctx.GasMeter().GasConsumed() + gasConsumedB = gasConsumedAfter - gasConsumedBefore + } + + s.Equalf( + fmt.Sprintf("%d", gasConsumedA), + fmt.Sprintf("%d", gasConsumedB), + "Gas consumed should be equal", + ) +} + +// See [Suite.TestGasConsumedInvariantSend]. +func (s *Suite) TestGasConsumedInvariantOther() { + to := evmtest.NewEthPrivAcc() // arbitrary constant + bankDenom := evm.EVMBankDenom + coins := sdk.NewCoins(sdk.NewInt64Coin(bankDenom, 420)) // arbitrary constant + // Use this value because the gas token is involved in both the BaseOp and + // AfterOp of the "ForceGasInvariant" function. + s.Run("MintCoins", func() { + FunctionalGasConsumedInvariantScenario{ + Setup: func(deps *evmtest.TestDeps) { + s.NoError( + testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, coins), + ) + }, + Measure: func(deps *evmtest.TestDeps) { + s.NoError( + deps.App.BankKeeper.MintCoins( + deps.Ctx, evm.ModuleName, coins, + ), + ) + }, + }.Run(s) + }) + + s.Run("BurnCoins", func() { + FunctionalGasConsumedInvariantScenario{ + Setup: func(deps *evmtest.TestDeps) { + s.NoError( + testapp.FundModuleAccount(deps.App.BankKeeper, deps.Ctx, evm.ModuleName, coins), + ) + }, + Measure: func(deps *evmtest.TestDeps) { + s.NoError( + deps.App.BankKeeper.BurnCoins( + deps.Ctx, evm.ModuleName, coins, + ), + ) + }, + }.Run(s) + }) + + s.Run("SendCoinsFromAccountToModule", func() { + FunctionalGasConsumedInvariantScenario{ + Setup: func(deps *evmtest.TestDeps) { + s.NoError( + testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, coins), + ) + }, + Measure: func(deps *evmtest.TestDeps) { + s.NoError( + deps.App.BankKeeper.SendCoinsFromAccountToModule( + deps.Ctx, deps.Sender.NibiruAddr, evm.ModuleName, coins, + ), + ) + }, + }.Run(s) + }) + + s.Run("SendCoinsFromModuleToAccount", func() { + FunctionalGasConsumedInvariantScenario{ + Setup: func(deps *evmtest.TestDeps) { + s.NoError( + testapp.FundModuleAccount(deps.App.BankKeeper, deps.Ctx, evm.ModuleName, coins), + ) + }, + Measure: func(deps *evmtest.TestDeps) { + s.NoError( + deps.App.BankKeeper.SendCoinsFromModuleToAccount( + deps.Ctx, evm.ModuleName, to.NibiruAddr, coins, + ), + ) + }, + }.Run(s) + }) + + s.Run("SendCoinsFromModuleToModule", func() { + FunctionalGasConsumedInvariantScenario{ + Setup: func(deps *evmtest.TestDeps) { + s.NoError( + testapp.FundModuleAccount(deps.App.BankKeeper, deps.Ctx, evm.ModuleName, coins), + ) + }, + Measure: func(deps *evmtest.TestDeps) { + s.NoError( + deps.App.BankKeeper.SendCoinsFromModuleToModule( + deps.Ctx, evm.ModuleName, staking.NotBondedPoolName, coins, + ), + ) + }, + }.Run(s) + }) +} + +// TestStateDBReadonlyInvariant: The EVM Keeper's "ApplyEvmMsg" function is used +// in both queries and transaction messages. Queries such as "eth_call", +// "eth_estimateGas", "debug_traceTransaction", "debugTraceCall", and +// "debug_traceBlock" interact with the EVM StateDB inside of "ApplyEvmMsg". +// +// Queries MUST NOT result in lingering effects on the blockchain multistore or +// the keepers, as doing so would result in potential inconsistencies between +// nodes and consensus failures. This test adds cases to make sure that invariant +// is held. +func (s *Suite) TestStateDBReadonlyInvariant() { + deps := evmtest.NewTestDeps() + _, _, erc20Contract := evmtest.DeployAndExecuteERC20Transfer(&deps, s.T()) + to := evmtest.NewEthPrivAcc() + + type StateDBWithExplanation struct { + StateDB *statedb.StateDB + Explanation string + } + + var stateDBs []StateDBWithExplanation + stateDBs = append(stateDBs, StateDBWithExplanation{ + StateDB: deps.App.EvmKeeper.Bank.StateDB, + Explanation: "initial DB after some EthereumTx", + }) + + s.T().Log("eth_call") + { + fungibleTokenContract := embeds.SmartContract_ERC20Minter + jsonTxArgs, err := json.Marshal(&evm.JsonTxArgs{ + From: &deps.Sender.EthAddr, + Data: (*hexutil.Bytes)(&fungibleTokenContract.Bytecode), + }) + s.Require().NoError(err) + req := &evm.EthCallRequest{Args: jsonTxArgs} + _, err = deps.EvmKeeper.EthCall(deps.GoCtx(), req) + s.Require().NoError(err) + stateDBs = append(stateDBs, StateDBWithExplanation{ + StateDB: deps.App.EvmKeeper.Bank.StateDB, + Explanation: "DB after eth_call query", + }) + } + + s.T().Log(`EthereumTx success, err == nil, vmError="insufficient balance for transfer"`) + { + balOfSender := deps.App.BankKeeper.GetBalance( + deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom) + tooManyTokensWei := evm.NativeToWei(balOfSender.Amount.AddRaw(420).BigInt()) + txTransferWei := evmtest.TxTransferWei{ + Deps: &deps, + To: to.EthAddr, + AmountWei: tooManyTokensWei, + } + evmResp, err := txTransferWei.Run() + s.Require().NoErrorf(err, "%#v", evmResp) + s.Require().Contains(evmResp.VmError, "insufficient balance for transfer") + stateDBs = append(stateDBs, StateDBWithExplanation{ + StateDB: deps.App.EvmKeeper.Bank.StateDB, + Explanation: "DB after EthereumTx with vmError", + }) + } + + s.T().Log(`EthereumTx success, err == nil, no vmError"`) + { + sendCoins := sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 420)) + s.NoError( + testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, sendCoins), + ) + + ctx := deps.Ctx + log.Log().Msgf("ctx.GasMeter().GasConsumed() %d", ctx.GasMeter().GasConsumed()) + log.Log().Msgf("ctx.GasMeter().Limit() %d", ctx.GasMeter().Limit()) + + wei := evm.NativeToWei(sendCoins[0].Amount.BigInt()) + evmResp, err := evmtest.TxTransferWei{ + Deps: &deps, + To: to.EthAddr, + AmountWei: wei, + }.Run() + + s.Require().NoErrorf(err, "%#v", evmResp) + s.Require().Falsef(evmResp.Failed(), "%#v", evmResp) + stateDBs = append(stateDBs, StateDBWithExplanation{ + StateDB: deps.App.EvmKeeper.Bank.StateDB, + Explanation: "DB after EthereumTx success", + }) + + for _, err := range []error{ + testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, sendCoins), + testapp.FundFeeCollector(deps.App.BankKeeper, deps.Ctx, + math.NewIntFromUint64(gethparams.TxGas), + ), + } { + s.NoError(err) + } + evmResp, err = evmtest.TxTransferWei{ + Deps: &deps, + To: erc20Contract, + AmountWei: wei, + GasLimit: gethparams.TxGas * 2, + }.Run() + s.Require().NoErrorf(err, "%#v", evmResp) + s.Require().Contains(evmResp.VmError, "execution reverted") + } + + s.T().Log("Verify that the NibiruBankKeeper.StateDB is unaffected") + var first *statedb.StateDB + for idx, db := range stateDBs { + if idx == 0 { + first = db.StateDB + continue + } + s.True(first == db.StateDB, db.Explanation) + } +} diff --git a/x/evm/keeper/call_contract.go b/x/evm/keeper/call_contract.go new file mode 100644 index 000000000..08928be64 --- /dev/null +++ b/x/evm/keeper/call_contract.go @@ -0,0 +1,96 @@ +package keeper + +import ( + "fmt" + "math/big" + "strings" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// CallContractWithInput invokes a smart contract with the given [contractInput] +// or deploys a new contract. +// +// Parameters: +// - ctx: The SDK context for the transaction. +// - fromAcc: The Ethereum address of the account initiating the contract call. +// - contract: Pointer to the Ethereum address of the contract. Nil if new +// contract is deployed. +// - commit: Boolean flag indicating whether to commit the transaction (true) +// or simulate it (false). +// - contractInput: Hexadecimal-encoded bytes use as input data to the call. +// +// Note: This function handles both contract method calls and simulations, +// depending on the 'commit' parameter. It uses a default gas limit. +func (k Keeper) CallContractWithInput( + ctx sdk.Context, + evmObj *vm.EVM, + fromAcc gethcommon.Address, + contract *gethcommon.Address, + commit bool, + contractInput []byte, + gasLimit uint64, +) (evmResp *evm.MsgEthereumTxResponse, err error) { + // This is a `defer` pattern to add behavior that runs in the case that the + // error is non-nil, creating a concise way to add extra information. + defer HandleOutOfGasPanic(&err, "CallContractError")() + nonce := k.GetAccNonce(ctx, fromAcc) + + unusedBigInt := big.NewInt(0) + evmMsg := gethcore.NewMessage( + fromAcc, + contract, + nonce, + unusedBigInt, // amount + gasLimit, + unusedBigInt, // gasFeeCap + unusedBigInt, // gasTipCap + unusedBigInt, // gasPrice + contractInput, + gethcore.AccessList{}, + !commit, // isFake + ) + + // Generating TxConfig with an empty tx hash as there is no actual eth tx + // sent by a user + txConfig := k.TxConfig(ctx, gethcommon.BigToHash(big.NewInt(0))) + evmResp, err = k.ApplyEvmMsg( + ctx, evmMsg, evmObj, evm.NewNoOpTracer(), commit, txConfig.TxHash, + ) + if evmResp != nil { + ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "CallContractWithInput") + } + if err != nil { + return nil, errors.Wrap(err, "failed to apply ethereum core message") + } + + if evmResp.Failed() { + if strings.Contains(evmResp.VmError, vm.ErrOutOfGas.Error()) { + err = fmt.Errorf("gas required exceeds allowance (%d)", gasLimit) + return + } + if evmResp.VmError == vm.ErrExecutionReverted.Error() { + err = fmt.Errorf("VMError: %w", evm.NewRevertError(evmResp.Ret)) + return + } + err = fmt.Errorf("VMError: %s", evmResp.VmError) + return + } + + // Success, update block gas used and bloom filter + if commit { + k.updateBlockBloom(ctx, evmResp, uint64(txConfig.LogIndex)) + + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err != nil { + return nil, errors.Wrap(err, "error emitting tx logs") + } + } + return evmResp, nil +} diff --git a/x/evm/keeper/erc20.go b/x/evm/keeper/erc20.go new file mode 100644 index 000000000..3851e06dd --- /dev/null +++ b/x/evm/keeper/erc20.go @@ -0,0 +1,307 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "bytes" + "fmt" + "math" + "math/big" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" +) + +const ( + // Erc20GasLimitDeploy only used internally when deploying ERC20Minter. + // Deployment requires ~1_600_000 gas + Erc20GasLimitDeploy uint64 = 2_000_000 + // Erc20GasLimitQuery used only for querying name, symbol and decimals + // Cannot be heavy. Only if the contract is malicious. + Erc20GasLimitQuery uint64 = 100_000 + // Erc20GasLimitExecute used for transfer, mint and burn. + // All must not exceed 200_000 + Erc20GasLimitExecute uint64 = 200_000 +) + +// getCallGas returns the gas limit for a call to an ERC20 contract following 63/64 rule (EIP-150) +// protection against recursive calls ERC20 -> precompile -> ERC20. +func getCallGasWithLimit(ctx sdk.Context, gasLimit uint64) uint64 { + availableGas := ctx.GasMeter().GasRemaining() + callGas := availableGas - uint64(math.Floor(float64(availableGas)/64)) + return min(callGas, gasLimit) +} + +// ERC20 returns a mutable reference to the keeper with an ERC20 contract ABI and +// Go functions corresponding to contract calls in the ERC20 standard like "mint" +// and "transfer" in the ERC20 standard. +func (k Keeper) ERC20() erc20Calls { + return erc20Calls{ + Keeper: &k, + ABI: embeds.SmartContract_ERC20Minter.ABI, + } +} + +type erc20Calls struct { + *Keeper + ABI *gethabi.ABI +} + +/* +Mint implements "ERC20Minter.mint" from ERC20Minter.sol. +See [nibiru/x/evm/embeds]. + + ```solidity + /// @dev Moves `amount` tokens from the caller's account to `to`. + /// Returns a boolean value indicating whether the operation succeeded. + /// Emits a {Transfer} event. + function mint(address to, uint256 amount) public virtual onlyOwner { + _mint(to, amount); + } + ``` + +[nibiru/x/evm/embeds]: https://github.com/NibiruChain/nibiru/v2/tree/main/x/evm/embeds +*/ +func (e erc20Calls) Mint( + erc20Contract, sender, recipient gethcommon.Address, amount *big.Int, + ctx sdk.Context, evmObj *vm.EVM, +) (evmResp *evm.MsgEthereumTxResponse, err error) { + contractInput, err := e.ABI.Pack("mint", recipient, amount) + if err != nil { + return nil, err + } + return e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute)) +} + +/* +Transfer implements "ERC20.transfer" + + ```solidity + /// @dev Moves `amount` tokens from the caller's account to `to`. + /// Returns a boolean value indicating whether the operation succeeded. + /// Emits a {Transfer} event. + function transfer(address to, uint256 amount) external returns (bool); + ``` +*/ +func (e erc20Calls) Transfer( + erc20Contract, sender, recipient gethcommon.Address, amount *big.Int, + ctx sdk.Context, evmObj *vm.EVM, +) (balanceIncrease *big.Int, resp *evm.MsgEthereumTxResponse, err error) { + recipientBalanceBefore, err := e.BalanceOf(erc20Contract, recipient, ctx, evmObj) + if err != nil { + return balanceIncrease, nil, errors.Wrap(err, "failed to retrieve recipient balance") + } + + contractInput, err := e.ABI.Pack("transfer", recipient, amount) + if err != nil { + return balanceIncrease, nil, err + } + resp, err = e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute)) + if err != nil { + return balanceIncrease, nil, err + } + + var erc20Bool ERC20Bool + err = e.ABI.UnpackIntoInterface(&erc20Bool, "transfer", resp.Ret) + if err != nil { + return balanceIncrease, nil, err + } + + // Handle the case of success=false: https://github.com/NibiruChain/nibiru/issues/2080 + success := erc20Bool.Value + if !success { + return balanceIncrease, nil, fmt.Errorf("transfer executed but returned success=false") + } + + recipientBalanceAfter, err := e.BalanceOf(erc20Contract, recipient, ctx, evmObj) + if err != nil { + return balanceIncrease, nil, errors.Wrap(err, "failed to retrieve recipient balance") + } + + balanceIncrease = new(big.Int).Sub(recipientBalanceAfter, recipientBalanceBefore) + + // For flexibility with fee on transfer tokens and other types of deductions, + // we cannot assume that the amount received by the recipient is equal to + // the call "amount". Instead, verify that the recipient got tokens and + // return the amount. + if balanceIncrease.Sign() <= 0 { + return balanceIncrease, nil, fmt.Errorf( + "amount of ERC20 tokens received MUST be positive: the balance of recipient %s would've changed by %v for token %s", + recipient.Hex(), balanceIncrease.String(), erc20Contract.Hex(), + ) + } + + return balanceIncrease, resp, err +} + +// BalanceOf retrieves the balance of an ERC20 token for a specific account. +// Implements "ERC20.balanceOf". +func (e erc20Calls) BalanceOf( + contract, account gethcommon.Address, + ctx sdk.Context, evmObj *vm.EVM, +) (out *big.Int, err error) { + return e.LoadERC20BigInt(ctx, evmObj, e.ABI, contract, "balanceOf", account) +} + +/* +Burn implements "ERC20Burnable.burn" + + ```solidity + /// @dev Destroys `amount` tokens from the caller. + function burn(uint256 amount) public virtual { + ``` +*/ +func (e erc20Calls) Burn( + erc20Contract, sender gethcommon.Address, amount *big.Int, + ctx sdk.Context, evmObj *vm.EVM, +) (evmResp *evm.MsgEthereumTxResponse, err error) { + contractInput, err := e.ABI.Pack("burn", amount) + if err != nil { + return nil, err + } + return e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute)) +} + +func (e erc20Calls) LoadERC20Name( + ctx sdk.Context, evmObj *vm.EVM, abi *gethabi.ABI, erc20 gethcommon.Address, +) (out string, err error) { + return e.loadERC20String(ctx, evmObj, abi, erc20, "name") +} + +func (e erc20Calls) LoadERC20Symbol( + ctx sdk.Context, evmObj *vm.EVM, abi *gethabi.ABI, erc20 gethcommon.Address, +) (out string, err error) { + return e.loadERC20String(ctx, evmObj, abi, erc20, "symbol") +} + +func (e erc20Calls) LoadERC20Decimals( + ctx sdk.Context, evmObj *vm.EVM, abi *gethabi.ABI, erc20 gethcommon.Address, +) (out uint8, err error) { + return e.loadERC20Uint8(ctx, evmObj, abi, erc20, "decimals") +} + +func (e erc20Calls) loadERC20String( + ctx sdk.Context, + evmObj *vm.EVM, + erc20Abi *gethabi.ABI, + erc20Contract gethcommon.Address, + methodName string, +) (out string, err error) { + input, err := erc20Abi.Pack(methodName) + if err != nil { + return out, err + } + evmResp, err := e.Keeper.CallContractWithInput( + ctx, + evmObj, + evm.EVM_MODULE_ADDRESS, + &erc20Contract, + false, + input, + getCallGasWithLimit(ctx, Erc20GasLimitQuery), + ) + if err != nil { + return out, err + } + + erc20Val := new(ERC20String) + if err := erc20Abi.UnpackIntoInterface( + erc20Val, methodName, evmResp.Ret, + ); err == nil { + return erc20Val.Value, err + } + + erc20Bytes32Val := new(ERC20Bytes32) + if err := erc20Abi.UnpackIntoInterface(erc20Bytes32Val, methodName, evmResp.Ret); err == nil { + return bytes32ToString(erc20Bytes32Val.Value), nil + } + + return "", fmt.Errorf("failed to decode response for method %s; unable to unpack as string or bytes32", methodName) +} + +func bytes32ToString(b [32]byte) string { + return string(bytes.Trim(b[:], "\x00")) +} + +func (e erc20Calls) loadERC20Uint8( + ctx sdk.Context, + evmObj *vm.EVM, + erc20Abi *gethabi.ABI, + erc20Contract gethcommon.Address, + methodName string, +) (out uint8, err error) { + input, err := erc20Abi.Pack(methodName) + if err != nil { + return out, err + } + evmResp, err := e.Keeper.CallContractWithInput( + ctx, + evmObj, + evm.EVM_MODULE_ADDRESS, + &erc20Contract, + false, + input, + getCallGasWithLimit(ctx, Erc20GasLimitQuery), + ) + if err != nil { + return out, err + } + + erc20Val := new(ERC20Uint8) + if err := erc20Abi.UnpackIntoInterface( + erc20Val, methodName, evmResp.Ret, + ); err == nil { + return erc20Val.Value, err + } + + erc20Uint256Val := new(ERC20BigInt) + if err := erc20Abi.UnpackIntoInterface( + erc20Uint256Val, methodName, evmResp.Ret, + ); err == nil { + // We can safely cast to uint8 because it's nonsense for decimals to be larger than 255 + return uint8(erc20Uint256Val.Value.Uint64()), err + } + + return 0, fmt.Errorf("failed to decode response for method %s; unable to unpack as uint8 or uint256", methodName) +} + +func (e erc20Calls) LoadERC20BigInt( + ctx sdk.Context, + evmObj *vm.EVM, + abi *gethabi.ABI, + contract gethcommon.Address, + methodName string, + args ...any, +) (out *big.Int, err error) { + input, err := abi.Pack(methodName, args...) + if err != nil { + return nil, err + } + evmResp, err := e.Keeper.CallContractWithInput( + ctx, + evmObj, + evm.EVM_MODULE_ADDRESS, + &contract, + false, + input, + getCallGasWithLimit(ctx, Erc20GasLimitQuery), + ) + if err != nil { + return nil, err + } + + erc20BigInt := new(ERC20BigInt) + err = abi.UnpackIntoInterface( + erc20BigInt, methodName, evmResp.Ret, + ) + if err != nil { + return nil, err + } + + return erc20BigInt.Value, nil +} diff --git a/x/evm/keeper/erc20_test.go b/x/evm/keeper/erc20_test.go new file mode 100644 index 000000000..18c50d77a --- /dev/null +++ b/x/evm/keeper/erc20_test.go @@ -0,0 +1,103 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "math/big" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *Suite) TestERC20Calls() { + deps := evmtest.NewTestDeps() + bankDenom := "ibc/btc" + funtoken := evmtest.CreateFunTokenForBankCoin(deps, bankDenom, &s.Suite) + erc20 := funtoken.Erc20Addr.Address + + s.Run("Mint tokens - Fail from non-owner", func() { + evmObj, _ := deps.NewEVM() + _, err := deps.EvmKeeper.ERC20().Mint( + erc20, deps.Sender.EthAddr, evm.EVM_MODULE_ADDRESS, + big.NewInt(69_420), deps.Ctx, evmObj, + ) + s.ErrorContains(err, "Ownable: caller is not the owner") + }) + + s.Run("successfully mint 69420 tokens", func() { + evmObj, stateDB := deps.NewEVM() + _, err := deps.EvmKeeper.ERC20().Mint( + erc20, /*erc20Addr*/ + evm.EVM_MODULE_ADDRESS, /*sender*/ + evm.EVM_MODULE_ADDRESS, /*recipient*/ + big.NewInt(69_420), /*amount*/ + deps.Ctx, + evmObj, + ) + s.Require().NoError(err) + s.Require().NoError(stateDB.Commit()) + + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(69_420), "expect 69420 tokens") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(0), "expect zero tokens") + }) + + s.Run("Transfer - Not enough funds", func() { + evmObj, _ := deps.NewEVM() + _, _, err := deps.EvmKeeper.ERC20().Transfer( + erc20, deps.Sender.EthAddr, evm.EVM_MODULE_ADDRESS, + big.NewInt(9_420), deps.Ctx, evmObj, + ) + s.ErrorContains(err, "ERC20: transfer amount exceeds balance") + // balances unchanged + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(69_420), "expect nonzero balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(0), "expect zero balance") + }) + + s.Run("Transfer - Success (sanity check)", func() { + evmObj, stateDB := deps.NewEVM() + sentAmt, _, err := deps.EvmKeeper.ERC20().Transfer( + erc20, /*erc20Addr*/ + evm.EVM_MODULE_ADDRESS, /*sender*/ + deps.Sender.EthAddr, /*recipient*/ + big.NewInt(9_420), /*amount*/ + deps.Ctx, + evmObj, + ) + s.Require().NoError(err) + s.Require().NoError(stateDB.Commit()) + evmtest.AssertERC20BalanceEqualWithDescription( + s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(9_420), "expect nonzero balance") + evmtest.AssertERC20BalanceEqualWithDescription( + s.T(), deps, evmObj, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(60_000), "expect nonzero balance") + s.Require().EqualValues(big.NewInt(9_420), sentAmt) + }) + + s.Run("Burn tokens - Allowed as non-owner", func() { + evmObj, stateDB := deps.NewEVM() + _, err := deps.EvmKeeper.ERC20().Burn( + erc20, /*erc20Addr*/ + deps.Sender.EthAddr, /*sender*/ + big.NewInt(6_000), /*amount*/ + deps.Ctx, + evmObj, + ) + s.Require().NoError(err) + s.Require().NoError(stateDB.Commit()) + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(3_420), "expect 3420 tokens") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(60_000), "expect 60000 tokens") + }) + + s.Run("Burn tokens - Allowed as owner", func() { + evmObj, stateDB := deps.NewEVM() + _, err := deps.EvmKeeper.ERC20().Burn( + erc20, /*erc20Addr*/ + evm.EVM_MODULE_ADDRESS, /*sender*/ + big.NewInt(6_000), /*amount*/ + deps.Ctx, + evmObj, + ) + s.Require().NoError(err) + s.Require().NoError(stateDB.Commit()) + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(3_420), "expect 3420 tokens") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(54_000), "expect 54000 tokens") + }) +} diff --git a/x/evm/keeper/evm_state.go b/x/evm/keeper/evm_state.go new file mode 100644 index 000000000..44d141150 --- /dev/null +++ b/x/evm/keeper/evm_state.go @@ -0,0 +1,170 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "fmt" + "math/big" + + "github.com/NibiruChain/collections" + "github.com/cosmos/cosmos-sdk/codec" + sdkstore "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +type ( + AccStatePrimaryKey = collections.Pair[gethcommon.Address, gethcommon.Hash] + CodeHash = []byte +) + +// EvmState isolates the key-value stores (collections) for the x/evm module. +type EvmState struct { + ModuleParams collections.Item[evm.Params] + + // ContractBytecode: Map from (byte)code hash -> contract bytecode + ContractBytecode collections.Map[CodeHash, []byte] + + // AccState: Map from eth address (account) and hash of a state key -> smart + // contract state. Each contract essentially has its own key-value store. + // + // - primary key (PK): (EthAddr+EthHash). The contract is the primary key + // because there's exactly one deployer and withdrawer. + // - value (V): State value bytes. + AccState collections.Map[ + AccStatePrimaryKey, // account (EthAddr) + state key (EthHash) + []byte, + ] + + // BlockLogSize: EVM tx log size for the block (transient). + BlockLogSize collections.ItemTransient[uint64] + // BlockTxIndex: EVM tx index for the block (transient). + BlockTxIndex collections.ItemTransient[uint64] + // BlockBloom: Bloom filters. + BlockBloom collections.ItemTransient[[]byte] +} + +func (k *Keeper) EVMState() EvmState { return k.EvmState } + +func NewEvmState( + cdc codec.BinaryCodec, + storeKey sdkstore.StoreKey, + storeKeyTransient sdkstore.StoreKey, +) EvmState { + return EvmState{ + ModuleParams: collections.NewItem( + storeKey, evm.KeyPrefixParams, + collections.ProtoValueEncoder[evm.Params](cdc), + ), + ContractBytecode: collections.NewMap( + storeKey, evm.KeyPrefixAccCodes, + eth.KeyEncoderBytes, + eth.ValueEncoderBytes, + ), + AccState: collections.NewMap( + storeKey, evm.KeyPrefixAccState, + collections.PairKeyEncoder(eth.KeyEncoderEthAddr, eth.KeyEncoderEthHash), + eth.ValueEncoderBytes, + ), + BlockLogSize: collections.NewItemTransient( + storeKeyTransient, + evm.NamespaceBlockLogSize, + collections.Uint64ValueEncoder, + ), + BlockBloom: collections.NewItemTransient( + storeKeyTransient, + evm.NamespaceBlockBloom, + eth.ValueEncoderBytes, + ), + BlockTxIndex: collections.NewItemTransient( + storeKeyTransient, + evm.NamespaceBlockTxIndex, + collections.Uint64ValueEncoder, + ), + } +} + +func (state EvmState) SetAccCode(ctx sdk.Context, codeHash, code []byte) { + if len(code) > 0 { + state.ContractBytecode.Insert(ctx, codeHash, code) + } else { + // Ignore collections "key not found" error because erasing an empty + // state is perfectly valid here. + _ = state.ContractBytecode.Delete(ctx, codeHash) + } +} + +func (state EvmState) GetContractBytecode( + ctx sdk.Context, codeHash []byte, +) (code []byte) { + return state.ContractBytecode.GetOr(ctx, codeHash, []byte{}) +} + +// GetParams returns the total set of evm parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params evm.Params) { + params, _ = k.EvmState.ModuleParams.Get(ctx) + return params +} + +// SetParams: Setter for the module parameters. +func (k Keeper) SetParams(ctx sdk.Context, params evm.Params) (err error) { + if params.CreateFuntokenFee.IsNegative() { + return fmt.Errorf("createFuntokenFee cannot be negative: %s", params.CreateFuntokenFee) + } + + k.EvmState.ModuleParams.Set(ctx, params) + return +} + +// SetState updates contract storage and deletes if the value is empty. +func (state EvmState) SetAccState( + ctx sdk.Context, addr gethcommon.Address, stateKey gethcommon.Hash, stateValue []byte, +) { + if len(stateValue) != 0 { + state.AccState.Insert(ctx, collections.Join(addr, stateKey), stateValue) + } else { + _ = state.AccState.Delete(ctx, collections.Join(addr, stateKey)) + } +} + +// GetState: Implements `statedb.Keeper` interface: Loads smart contract state. +func (k *Keeper) GetState(ctx sdk.Context, addr gethcommon.Address, stateKey gethcommon.Hash) gethcommon.Hash { + return gethcommon.BytesToHash(k.EvmState.AccState.GetOr( + ctx, + collections.Join(addr, stateKey), + []byte{}, + )) +} + +// GetBlockBloomTransient returns bloom bytes for the current block height +func (state EvmState) GetBlockBloomTransient(ctx sdk.Context) *big.Int { + bloomBz, err := state.BlockBloom.Get(ctx) + if err != nil { + return big.NewInt(0) + } + return new(big.Int).SetBytes(bloomBz) +} + +func (state EvmState) CalcBloomFromLogs( + ctx sdk.Context, newLogs []*gethcore.Log, +) (bloom gethcore.Bloom) { + if len(newLogs) > 0 { + bloomInt := state.GetBlockBloomTransient(ctx) + bloomInt.Or(bloomInt, big.NewInt(0).SetBytes(gethcore.LogsBloom(newLogs))) + bloom = gethcore.BytesToBloom(bloomInt.Bytes()) + } + return bloom +} + +// GetAccNonce returns the sequence number of an account, returns 0 if not exists. +func (k Keeper) GetAccNonce(ctx sdk.Context, addr gethcommon.Address) uint64 { + nibiruAddr := sdk.AccAddress(addr.Bytes()) + acct := k.accountKeeper.GetAccount(ctx, nibiruAddr) + if acct == nil { + return 0 + } + return acct.GetSequence() +} diff --git a/x/evm/keeper/funtoken_from_coin.go b/x/evm/keeper/funtoken_from_coin.go new file mode 100644 index 000000000..6cb4977d5 --- /dev/null +++ b/x/evm/keeper/funtoken_from_coin.go @@ -0,0 +1,118 @@ +package keeper + +import ( + "fmt" + "math/big" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" +) + +func (k *Keeper) createFunTokenFromCoin( + ctx sdk.Context, bankDenom string, +) (funtoken *evm.FunToken, err error) { + // 1 | Coin already registered with FunToken? + if funtokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, bankDenom)); len(funtokens) > 0 { + return nil, fmt.Errorf("funtoken mapping already created for bank denom \"%s\"", bankDenom) + } + + // 2 | Check for denom metadata in bank state + bankMetadata, isFound := k.Bank.GetDenomMetaData(ctx, bankDenom) + if !isFound { + return nil, fmt.Errorf("bank coin denom should have bank metadata for denom \"%s\"", bankDenom) + } + + // 3 | deploy ERC20 for metadata + erc20Addr, err := k.deployERC20ForBankCoin(ctx, bankMetadata) + if err != nil { + return nil, errors.Wrap(err, "failed to deploy ERC20 for bank coin") + } + + // 4 | ERC20 already registered with FunToken? + if funtokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20Addr)); len(funtokens) > 0 { + return nil, fmt.Errorf("funtoken mapping already created for ERC20 \"%s\"", erc20Addr.Hex()) + } + + // 5 | Officially create the funtoken mapping + funtoken = &evm.FunToken{ + Erc20Addr: eth.EIP55Addr{ + Address: erc20Addr, + }, + BankDenom: bankDenom, + IsMadeFromCoin: true, + } + + return funtoken, k.FunTokens.SafeInsert( + ctx, erc20Addr, + funtoken.BankDenom, + funtoken.IsMadeFromCoin, + ) +} + +func (k *Keeper) deployERC20ForBankCoin( + ctx sdk.Context, bankCoin bank.Metadata, +) (erc20Addr gethcommon.Address, err error) { + erc20Addr = crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS)) + + // bank.Metadata validation guarantees that both "Base" and "Display" denoms + // pass "sdk.ValidateDenom" and that the "DenomUnits" slice has exponents in + // ascending order with at least one element, which must be the base + // denomination and have exponent 0. + decimals := uint8(0) + if len(bankCoin.DenomUnits) > 0 { + decimalsIdx := len(bankCoin.DenomUnits) - 1 + decimals = uint8(bankCoin.DenomUnits[decimalsIdx].Exponent) + } + + // pass empty method name to deploy the contract + packedArgs, err := embeds.SmartContract_ERC20Minter.ABI.Pack("", bankCoin.Name, bankCoin.Symbol, decimals) + if err != nil { + return gethcommon.Address{}, errors.Wrap(err, "failed to pack ABI args") + } + input := append(embeds.SmartContract_ERC20Minter.Bytecode, packedArgs...) + + evmMsg := gethcore.NewMessage( + evm.EVM_MODULE_ADDRESS, + nil, /*contract*/ + k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS), + big.NewInt(0), /*amount*/ + Erc20GasLimitDeploy, + big.NewInt(0), /*gasFeeCap*/ + big.NewInt(0), /*gasTipCap*/ + big.NewInt(0), /*gasPrice*/ + input, + gethcore.AccessList{}, + false, /*isFake*/ + ) + evmCfg := k.GetEVMConfig(ctx) + txConfig := k.TxConfig(ctx, gethcommon.BigToHash(big.NewInt(0))) + stateDB := k.Bank.StateDB + if stateDB == nil { + stateDB = k.NewStateDB(ctx, txConfig) + } + defer func() { + k.Bank.StateDB = nil + }() + evmObj := k.NewEVM(ctx, evmMsg, evmCfg, nil /*tracer*/, stateDB) + _, err = k.CallContractWithInput( + ctx, evmObj, evm.EVM_MODULE_ADDRESS, nil, true /*commit*/, input, Erc20GasLimitDeploy, + ) + if err != nil { + return gethcommon.Address{}, errors.Wrap(err, "failed to deploy ERC20 contract") + } + + err = stateDB.Commit() + if err != nil { + return gethcommon.Address{}, errors.Wrap(err, "failed to commit stateDB") + } + + return erc20Addr, nil +} diff --git a/x/evm/keeper/funtoken_from_coin_test.go b/x/evm/keeper/funtoken_from_coin_test.go new file mode 100644 index 000000000..32563b002 --- /dev/null +++ b/x/evm/keeper/funtoken_from_coin_test.go @@ -0,0 +1,851 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { + deps := evmtest.NewTestDeps() + s.Run("Compute contract address. FindERC20 should fail", func() { + evmObj, _ := deps.NewEVM() + metadata, err := deps.EvmKeeper.FindERC20Metadata( + deps.Ctx, + evmObj, + crypto.CreateAddress( + evm.EVM_MODULE_ADDRESS, deps.EvmKeeper.GetAccNonce(deps.Ctx, evm.EVM_MODULE_ADDRESS)), + nil, + ) + s.Require().Error(err) + s.Require().Nil(metadata) + }) + + s.T().Log("Setup: Create a coin in the bank state") + bankDenom := "sometoken" + deps.App.BankKeeper.SetDenomMetaData(deps.Ctx, bank.Metadata{ + DenomUnits: []*bank.DenomUnit{ + { + Denom: bankDenom, + Exponent: 0, + Aliases: nil, + }, + }, + Base: bankDenom, + Display: bankDenom, + Name: bankDenom, + Symbol: "TOKEN", + }) + + s.Run("insufficient funds to create funtoken", func() { + s.T().Log("sad: not enough funds to create fun token") + _, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: bankDenom, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().ErrorContains(err, "insufficient funds") + }) + + s.Run("invalid bank denom", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + _, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: "doesn't exist", + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().Error(err) + }) + + s.Run("happy: CreateFunToken for the bank coin", func() { + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + expectedErc20Addr := crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, deps.EvmKeeper.GetAccNonce(deps.Ctx, evm.EVM_MODULE_ADDRESS)) + createFuntokenResp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: bankDenom, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + + s.Equal( + createFuntokenResp.FuntokenMapping, + evm.FunToken{ + Erc20Addr: eth.EIP55Addr{Address: expectedErc20Addr}, + BankDenom: bankDenom, + IsMadeFromCoin: true, + }, + ) + actualErc20Addr := createFuntokenResp.FuntokenMapping.Erc20Addr + + s.T().Log("Expect ERC20 to be deployed") + _, err = deps.EvmKeeper.Code(deps.Ctx, &evm.QueryCodeRequest{ + Address: actualErc20Addr.String(), + }) + s.Require().NoError(err) + + s.T().Log("Expect ERC20 metadata on contract") + evmObj, _ := deps.NewEVM() + info, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, evmObj, actualErc20Addr.Address, nil) + s.Require().NoError(err, info) + s.Equal( + keeper.ERC20Metadata{ + Name: bankDenom, + Symbol: "TOKEN", + Decimals: 0, + }, *info, + ) + + // Event "EventFunTokenCreated" must present + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventFunTokenCreated{ + BankDenom: bankDenom, + Erc20ContractAddress: actualErc20Addr.String(), + Creator: deps.Sender.NibiruAddr.String(), + IsMadeFromCoin: true, + }, + ) + }) + + s.Run("sad: CreateFunToken for the bank coin: already registered", func() { + // Give the sender funds for the fee + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + _, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: bankDenom, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().ErrorContains(err, "funtoken mapping already created") + }) +} + +func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + alice := evmtest.NewEthPrivAcc() + + // Initial setup + funToken := s.fundAndCreateFunToken(deps, 100) + + s.T().Log("Convert bank coin to erc-20") + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + _, err := deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10)), + ToEthAddr: eth.EIP55Addr{ + Address: alice.EthAddr, + }, + }, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + + s.T().Log("Check typed event") + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20ContractAddress: funToken.Erc20Addr.String(), + ToEthAddr: alice.EthAddr.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10)), + }, + ) + + // Check 1: module balance + moduleBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), evm.EVMBankDenom) + s.Require().Equal(sdk.NewInt(10), moduleBalance.Amount) + + // Check 2: Sender balance + senderBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom) + s.Require().Equal(sdk.NewInt(90), senderBalance.Amount) + + // Check 3: erc-20 balance + balance, err := deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx, evmObj) + s.Require().NoError(err) + s.Require().Zero(balance.Cmp(big.NewInt(10))) + + s.Run("sad: Convert more bank coin to erc-20, insufficient funds", func() { + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(100)), + ToEthAddr: eth.EIP55Addr{ + Address: alice.EthAddr, + }, + }, + ) + s.Require().ErrorContains(err, "insufficient funds") + }) + + s.T().Log("Convert erc-20 to back to bank coin") + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( + "sendToBank", + funToken.Erc20Addr.Address, + big.NewInt(10), + deps.Sender.NibiruAddr.String(), + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + alice.EthAddr, // from + &precompile.PrecompileAddr_FunToken, // to + true, // commit + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + + // Check 1: module balance + moduleBalance = deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), evm.EVMBankDenom) + s.Require().True(moduleBalance.Amount.Equal(sdk.ZeroInt())) + + // Check 2: Sender balance + senderBalance = deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom) + s.Require().Equal(sdk.NewInt(100), senderBalance.Amount) + + // Check 3: erc-20 balance + balance, err = deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx, evmObj) + s.Require().NoError(err) + s.Require().Equal("0", balance.String()) + + s.T().Log("sad: Convert more erc-20 to back to bank coin, insufficient funds") + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + alice.EthAddr, // from + &precompile.PrecompileAddr_FunToken, // to + true, // commit + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().ErrorContains(err, "transfer amount exceeds balance") + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) +} + +// TestNativeSendThenPrecompileSend tests a race condition where the state DB +// commit may overwrite the state after the precompile execution, potentially +// causing a loss of funds. +// +// The order of operations is to: +// 1. Create a funtoken mapping from NIBI, a bank coin. +// 2. Use a test Solidity contract to perform two transfers in a single call: a +// transfer of NIBI with native send and a precompile "IFunToken.sendToBank" +// transfer for the same asset. +// +// INITIAL STATE: +// - Test contract funds: 10 NIBI, 10 WNIBI +// CONTRACT CALL: +// - Sends 10 NIBI natively and 10 WNIBI -> NIBI to Alice using precompile +// EXPECTED: +// - Test contract funds: 0 NIBI, 0 WNIBI +// - Alice: 20 NIBI +// - Module account: 0 NIBI escrowed +func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + bankDenom := evm.EVMBankDenom + + // Initial setup + sendAmt := big.NewInt(10) + funtoken := s.fundAndCreateFunToken(deps, sendAmt.Int64()) + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestNativeSendThenPrecompileSendJson, + funtoken.Erc20Addr.Address, + ) + s.Require().NoError(err) + + testContractAddr := deployResp.ContractAddr + testContractNibiAddr := eth.EthAddrToNibiruAddr(testContractAddr) + + s.T().Log("Give the test contract 10 NIBI (native)") + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + testContractNibiAddr, + sdk.NewCoins(sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(sendAmt)))), + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, bankDenom, testContractAddr, sendAmt, "expect 10 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, bankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect 0 balance", + ) + + s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(sendAmt)), + ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, + }, + ) + s.Require().NoError(err) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: testContractAddr, + BalanceBank: sendAmt, + BalanceERC20: sendAmt, + }.Assert(s.T(), deps, evmObj) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: sendAmt, + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + // Alice hex and Alice bech32 is the same address in different representation, + // so funds are expected to be available in Alice's bank wallet + alice := evmtest.NewEthPrivAcc() + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: alice.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + s.T().Log("call test contract") + newSendAmtSendToBank := new(big.Int).Quo(sendAmt, big.NewInt(2)) + newSendAmtEvmTransfer := evm.NativeToWei(newSendAmtSendToBank) + + contractInput, err := embeds.SmartContract_TestNativeSendThenPrecompileSendJson.ABI.Pack( + "nativeSendThenPrecompileSend", + alice.EthAddr, /*to*/ + newSendAmtEvmTransfer, /*amount*/ + alice.NibiruAddr.String(), /*to*/ + newSendAmtSendToBank, /*amount*/ + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + s.Empty(evmResp.VmError) + gasUsedFor2Ops := evmResp.GasUsed + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: alice.EthAddr, + BalanceBank: new(big.Int).Mul( + newSendAmtSendToBank, big.NewInt(2)), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: testContractAddr, + BalanceBank: big.NewInt(5), + BalanceERC20: big.NewInt(5), + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(5), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + contractInput, err = embeds.SmartContract_TestNativeSendThenPrecompileSendJson.ABI.Pack( + "justPrecompileSend", + alice.NibiruAddr.String(), /*to*/ + newSendAmtSendToBank, /*amount*/ + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + evmResp, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.DefaultEthCallGasLimit, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + s.Empty(evmResp.VmError) + gasUsedFor1Op := evmResp.GasUsed + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: alice.EthAddr, + BalanceBank: new(big.Int).Mul( + newSendAmtSendToBank, big.NewInt(3)), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: testContractAddr, + BalanceBank: big.NewInt(5), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + s.Require().Greater(gasUsedFor2Ops, gasUsedFor1Op, "2 operations should consume more gas") +} + +// TestERC20TransferThenPrecompileSend +// 1. Creates a funtoken from coin. +// 2. Using the test contract, performs two sends in a single call: a erc20 +// transfer and a precompile sendToBank. +// It tests a race condition where the state DB commit may overwrite the state after the precompile execution, +// potentially causing an infinite minting of funds. +// +// INITIAL STATE: +// - Test contract funds: 10 WNIBI +// CONTRACT CALL: +// - Sends 1 WNIBI to Alice using erc20 transfer and 9 WNIBI -> NIBI to Alice using precompile +// EXPECTED: +// - Test contract funds: 0 WNIBI +// - Alice: 1 WNIBI, 9 NIBI +// - Module account: 1 NIBI escrowed (which Alice holds as 1 WNIBI) +func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + + funToken := s.fundAndCreateFunToken(deps, 10e6) + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestERC20TransferThenPrecompileSend, + funToken.Erc20Addr.Address, + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)), + ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, + }, + ) + s.Require().NoError(err) + + // check balances + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: testContractAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(10e6), + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + // Alice hex and Alice bech32 is the same address in different representation + alice := evmtest.NewEthPrivAcc() + + s.T().Log("call test contract") + contractInput, err := embeds.SmartContract_TestERC20TransferThenPrecompileSend.ABI.Pack( + "erc20TransferThenPrecompileSend", + alice.EthAddr, /*to*/ + big.NewInt(1e6), /*amount*/ + alice.NibiruAddr.String(), /*to*/ + big.NewInt(9e6), /*amount*/ + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, // from + &testContractAddr, // to + true, // commit + contractInput, + 10_000_000, // gas limit + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: alice.EthAddr, + BalanceBank: big.NewInt(9e6), + BalanceERC20: big.NewInt(1e6), + Description: "Alice has 9 NIBI / 1 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: testContractAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Test contract 0 NIBI / 0 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(1e6), + BalanceERC20: big.NewInt(0e6), + Description: "Module account has 1 NIBI escrowed", + }.Assert(s.T(), deps, evmObj) +} + +// TestPrecompileSelfCallRevert +// 1. Creates a funtoken from coin. +// 2. Using the test contract, creates another instance of itself, calls the precompile method and then force reverts. +// It tests a race condition where the state DB commit +// may save the wrong state before the precompile execution, not revert it entirely, +// potentially causing an infinite mint of funds. +// +// INITIAL STATE: +// - Test contract funds: 10 NIBI, 10 WNIBI +// CONTRACT CALL: +// - Sends 1 NIBI to Alice using native send and 1 WNIBI -> NIBI to Charles using precompile +// EXPECTED: +// - all changes reverted +// - Test contract funds: 10 NIBI, 10 WNIBI +// - Alice: 0 NIBI +// - Charles: 0 NIBI +// - Module account: 10 NIBI escrowed (which Test contract holds as 10 WNIBI) +func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() { + deps := evmtest.NewTestDeps() + + // Initial setup + funToken := s.fundAndCreateFunToken(deps, 10e6) + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestPrecompileSelfCallRevert, + funToken.Erc20Addr.Address, + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)), + ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, + }, + ) + s.Require().NoError(err) + + s.T().Log("Give the test contract 10 NIBI (native)") + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + eth.EthAddrToNibiruAddr(testContractAddr), + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))), + )) + + evmObj, _ := deps.NewEVM() + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: testContractAddr, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(10e6), + Description: "Initial contract state sanity check: 10 NIBI / 10 WNIBI", + }.Assert(s.T(), deps, evmObj) + + // Create Alice and Charles. Contract will try to send Alice native coins and + // send Charles tokens via sendToBank + alice := evmtest.NewEthPrivAcc() + charles := evmtest.NewEthPrivAcc() + + s.T().Log("call test contract") + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + contractInput, err := embeds.SmartContract_TestPrecompileSelfCallRevert.ABI.Pack( + "selfCallTransferFunds", + alice.EthAddr, + evm.NativeToWei(big.NewInt(1e6)), + charles.NibiruAddr.String(), + big.NewInt(9e6), + ) + s.Require().NoError(err) + evpResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evpResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: alice.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Alice has 0 NIBI / 0 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: charles.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Charles has 0 NIBI / 0 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: testContractAddr, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(10e6), + Description: "Test contract has 10 NIBI / 10 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(0), + Description: "Module account has 10 NIBI escrowed", + }.Assert(s.T(), deps, evmObj) +} + +// TestPrecompileSelfCallRevert +// 1. Creates a funtoken from coin. +// 2. Calls the test contract +// a. sendToBank +// b. erc20 transfer +// +// INITIAL STATE: +// - Test contract funds: 10 WNIBI +// CONTRACT CALL: +// - Sends 10 WNIBI to Alice, and try to send 1 NIBI to Bob +// EXPECTED: +// - all changes reverted because of not enough balance +// - Test contract funds: 10 WNIBI +// - Alice: 10 WNIBI +// - Bob: 0 NIBI +// - Module account: 10 NIBI escrowed (which Test contract holds as 10 WNIBI) +func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() { + deps := evmtest.NewTestDeps() + + // Initial setup + funToken := s.fundAndCreateFunToken(deps, 10e6) + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestPrecompileSendToBankThenERC20Transfer, + funToken.Erc20Addr.Address, + deps.Sender.NibiruAddr.String(), + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)), + ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, + }, + ) + s.Require().NoError(err) + + // Create Alice and Bob. Contract will try to send Alice native coins and + // send Bob ERC20 tokens. + alice := evmtest.NewEthPrivAcc() + bob := evmtest.NewEthPrivAcc() + + s.T().Log("call test contract") + contractInput, err := embeds.SmartContract_TestPrecompileSendToBankThenERC20Transfer.ABI.Pack( + "attack", + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evpResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().ErrorContains(err, "execution reverted") + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evpResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: alice.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Alice has 0 NIBI / 0 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: bob.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Bob has 0 NIBI / 0 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: testContractAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(10e6), + Description: "Test contract has 10 NIBI / 10 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(0), + Description: "Module account has 10 NIBI escrowed", + }.Assert(s.T(), deps, evmObj) +} + +// fundAndCreateFunToken creates initial setup for tests +func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, unibiAmount int64) evm.FunToken { + bankDenom := evm.EVMBankDenom + + s.T().Log("Setup: Create a coin in the bank state") + deps.App.BankKeeper.SetDenomMetaData(deps.Ctx, bank.Metadata{ + DenomUnits: []*bank.DenomUnit{ + { + Denom: bankDenom, + Exponent: 0, + }, + { + Denom: "NIBI", + Exponent: 6, + }, + }, + Base: bankDenom, + Display: "NIBI", + Name: "NIBI", + Symbol: "NIBI", + }) + + s.T().Log("Give the sender funds for funtoken creation and funding test contract") + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx).Add(sdk.NewCoin(bankDenom, sdk.NewInt(unibiAmount))), + )) + + s.T().Log("Create FunToken from coin") + createFunTokenResp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: bankDenom, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + + return createFunTokenResp.FuntokenMapping +} + +type FunTokenFromCoinSuite struct { + suite.Suite +} + +func TestFunTokenFromCoinSuite(t *testing.T) { + suite.Run(t, new(FunTokenFromCoinSuite)) +} diff --git a/x/evm/keeper/funtoken_from_erc20.go b/x/evm/keeper/funtoken_from_erc20.go new file mode 100644 index 000000000..36c071768 --- /dev/null +++ b/x/evm/keeper/funtoken_from_erc20.go @@ -0,0 +1,244 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "fmt" + "math/big" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +// FindERC20Metadata retrieves the metadata of an ERC20 token. +// +// Parameters: +// - ctx: The SDK context for the transaction. +// - contract: The Ethereum address of the ERC20 contract. +// +// Returns: +// - info: ERC20Metadata containing name, symbol, and decimals. +// - err: An error if metadata retrieval fails. +func (k Keeper) FindERC20Metadata( + ctx sdk.Context, + evmObj *vm.EVM, + contract gethcommon.Address, + abi *gethabi.ABI, +) (info *ERC20Metadata, err error) { + effectiveAbi := embeds.SmartContract_ERC20Minter.ABI + + if abi != nil { + effectiveAbi = abi + } + // Load name, symbol, decimals + name, err := k.ERC20().LoadERC20Name(ctx, evmObj, effectiveAbi, contract) + if err != nil { + return nil, err + } + + symbol, err := k.ERC20().LoadERC20Symbol(ctx, evmObj, effectiveAbi, contract) + if err != nil { + return nil, err + } + + decimals, err := k.ERC20().LoadERC20Decimals(ctx, evmObj, effectiveAbi, contract) + if err != nil { + return nil, err + } + + return &ERC20Metadata{ + Name: name, + Symbol: symbol, + Decimals: decimals, + }, nil +} + +// ERC20Metadata: Optional metadata fields parsed from an ERC20 contract. +// The [Wrapped Ether contract] is a good example for reference. +// +// ```solidity +// constract WETH9 { +// string public name = "Wrapped Ether"; +// string public symbol = "WETH" +// uint8 public decimals = 18; +// } +// ``` +// +// Note that the name and symbol fields may be empty, according to the [ERC20 +// specification]. +// +// [Wrapped Ether contract]: https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code +// [ERC20 specification]: https://eips.ethereum.org/EIPS/eip-20 +type ERC20Metadata struct { + Name string + Symbol string + Decimals uint8 +} + +type ( + ERC20String struct{ Value string } + // ERC20Uint8: Unpacking type for "uint8" from Solidity. This is only used in + // the "ERC20.decimals" function. + ERC20Uint8 struct{ Value uint8 } + ERC20Bool struct{ Value bool } + // ERC20BigInt: Unpacking type for "uint256" from Solidity. + ERC20BigInt struct{ Value *big.Int } + ERC20Bytes32 struct{ Value [32]byte } +) + +// createFunTokenFromERC20 creates a new FunToken mapping from an existing ERC20 token. +// +// This function performs the following steps: +// 1. Checks if the ERC20 token is already registered as a FunToken. +// 2. Retrieves the metadata of the existing ERC20 token. +// 3. Verifies that the corresponding bank coin denom is not already registered. +// 4. Sets the bank coin denom metadata in the state. +// 5. Creates and inserts the new FunToken mapping. +// +// Parameters: +// - ctx: The SDK context for the transaction. +// - erc20: The Ethereum address of the ERC20 token in HexAddr format. +// +// Returns: +// - funtoken: The created FunToken mapping. +// - err: An error if any step fails, nil otherwise. +// +// Possible errors: +// - If the ERC20 token is already registered as a FunToken. +// - If the ERC20 metadata cannot be retrieved. +// - If the bank coin denom is already registered. +// - If the bank metadata validation fails. +// - If the FunToken insertion fails. +func (k *Keeper) createFunTokenFromERC20( + ctx sdk.Context, erc20 gethcommon.Address, +) (funtoken *evm.FunToken, err error) { + // 1 | ERC20 already registered with FunToken? + if funtokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20)); len(funtokens) > 0 { + return nil, fmt.Errorf("funtoken mapping already created for ERC20 \"%s\"", erc20) + } + + // 2 | Get existing ERC20 metadata + // We use dummy values for the tx config and evm config because we aren't in an actual end user transaction, it's just a state query. + stateDB := k.Bank.StateDB + if stateDB == nil { + stateDB = k.NewStateDB(ctx, statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash()))) + } + defer func() { + k.Bank.StateDB = nil + }() + evmMsg := gethcore.NewMessage( + evm.EVM_MODULE_ADDRESS, + &erc20, + 0, + big.NewInt(0), + 0, + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + []byte{}, + gethcore.AccessList{}, + false, + ) + evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), evm.NewNoOpTracer(), stateDB) + erc20Info, err := k.FindERC20Metadata(ctx, evmObj, erc20, nil) + if err != nil { + return nil, err + } + + bankDenom := fmt.Sprintf("erc20/%s", erc20.String()) + + // 3 | Coin already registered with FunToken? + _, isFound := k.Bank.GetDenomMetaData(ctx, bankDenom) + if isFound { + return nil, fmt.Errorf("bank coin denom already registered with denom \"%s\"", bankDenom) + } + if funtokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, bankDenom)); len(funtokens) > 0 { + return nil, fmt.Errorf("funtoken mapping already created for bank denom \"%s\"", bankDenom) + } + + // 4 | Set bank coin denom metadata in state + bankMetadata := erc20Info.ToBankMetadata(bankDenom, erc20) + + err = bankMetadata.Validate() + if err != nil { + return nil, fmt.Errorf("failed to validate bank metadata: %w", err) + } + k.Bank.SetDenomMetaData(ctx, bankMetadata) + + // 5 | Officially create the funtoken mapping + funtoken = &evm.FunToken{ + Erc20Addr: eth.EIP55Addr{ + Address: erc20, + }, + BankDenom: bankDenom, + IsMadeFromCoin: false, + } + + err = stateDB.Commit() + if err != nil { + return nil, errors.Wrap(err, "failed to commit stateDB") + } + + return funtoken, k.FunTokens.SafeInsert( + ctx, erc20, bankDenom, false, + ) +} + +// ToBankMetadata produces the "bank.Metadata" corresponding to a FunToken +// mapping created from an ERC20 token. +// +// The first argument of DenomUnits is required and the official base unit +// onchain, meaning the denom must be equivalent to bank.Metadata.Base. +// +// Decimals for an ERC20 are synonymous to "bank.DenomUnit.Exponent" in what +// they mean for external clients like wallets. +func (erc20Info ERC20Metadata) ToBankMetadata( + bankDenom string, erc20 gethcommon.Address, +) bank.Metadata { + var symbol string + if erc20Info.Symbol != "" { + symbol = erc20Info.Symbol + } else { + symbol = bankDenom + } + + var name string + if erc20Info.Name != "" { + name = erc20Info.Name + } else { + name = bankDenom + } + + denomUnits := []*bank.DenomUnit{ + { + Denom: bankDenom, + Exponent: 0, + }, + } + display := symbol + if erc20Info.Decimals > 0 { + denomUnits = append(denomUnits, &bank.DenomUnit{ + Denom: display, + Exponent: uint32(erc20Info.Decimals), + }) + } + return bank.Metadata{ + Description: fmt.Sprintf( + "ERC20 token \"%s\" represented as a Bank Coin with a corresponding FunToken mapping", erc20.String(), + ), + DenomUnits: denomUnits, + Base: bankDenom, + Display: display, + Name: name, + Symbol: symbol, + } +} diff --git a/x/evm/keeper/funtoken_from_erc20_test.go b/x/evm/keeper/funtoken_from_erc20_test.go new file mode 100644 index 000000000..86960a2f3 --- /dev/null +++ b/x/evm/keeper/funtoken_from_erc20_test.go @@ -0,0 +1,641 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "encoding/hex" + "fmt" + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { + deps := evmtest.NewTestDeps() + + // assert that the ERC20 contract is not deployed + expectedERC20Addr := crypto.CreateAddress(deps.Sender.EthAddr, deps.NewStateDB().GetNonce(deps.Sender.EthAddr)) + + s.T().Log("Deploy ERC20") + metadata := keeper.ERC20Metadata{ + Name: "erc20name", + Symbol: "TOKEN", + Decimals: 18, + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_ERC20Minter, + metadata.Name, metadata.Symbol, metadata.Decimals, + ) + s.Require().NoError(err) + s.Require().Equal(expectedERC20Addr, deployResp.ContractAddr) + + evmObj, _ := deps.NewEVM() + actualMetadata, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, evmObj, deployResp.ContractAddr, nil) + s.Require().NoError(err) + s.Require().Equal(metadata, *actualMetadata) + + _, err = deps.EvmKeeper.Code(deps.Ctx, &evm.QueryCodeRequest{ + Address: expectedERC20Addr.String(), + }) + s.Require().NoError(err) + + erc20Addr := eth.EIP55Addr{ + Address: deployResp.ContractAddr, + } + + s.Run("sad: insufficient funds to create FunToken mapping", func() { + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: &erc20Addr, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().ErrorContains(err, "insufficient funds") + }) + + s.Run("happy: CreateFunToken for the ERC20", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + resp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: &erc20Addr, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err, "erc20 %s", erc20Addr) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + + expectedBankDenom := fmt.Sprintf("erc20/%s", expectedERC20Addr.String()) + s.Equal( + resp.FuntokenMapping, + evm.FunToken{ + Erc20Addr: erc20Addr, + BankDenom: expectedBankDenom, + IsMadeFromCoin: false, + }) + + // Event "EventFunTokenCreated" must present + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventFunTokenCreated{ + BankDenom: expectedBankDenom, + Erc20ContractAddress: erc20Addr.String(), + Creator: deps.Sender.NibiruAddr.String(), + IsMadeFromCoin: false, + }, + ) + + bankDenomMetadata, _ := deps.App.BankKeeper.GetDenomMetaData(deps.Ctx, expectedBankDenom) + s.Require().Equal(bank.Metadata{ + Description: fmt.Sprintf( + "ERC20 token \"%s\" represented as a Bank Coin with a corresponding FunToken mapping", erc20Addr.String(), + ), + DenomUnits: []*bank.DenomUnit{ + {Denom: expectedBankDenom, Exponent: 0}, + {Denom: metadata.Symbol, Exponent: uint32(metadata.Decimals)}, + }, + Base: expectedBankDenom, + Display: metadata.Symbol, + Name: metadata.Name, + Symbol: metadata.Symbol, + URI: "", + URIHash: "", + }, bankDenomMetadata) + }) + + s.Run("sad: CreateFunToken for the ERC20: already registered", func() { + // Give the sender funds for the fee + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: &erc20Addr, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.ErrorContains(err, "funtoken mapping already created") + }) + + s.Run("sad: CreateFunToken for the ERC20: invalid sender", func() { + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: &erc20Addr, + }, + ) + s.ErrorContains(err, "invalid sender") + }) + + s.Run("sad: CreateFunToken for the ERC20: missing erc20 address", func() { + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: nil, + FromBankDenom: "", + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.ErrorContains(err, "either the \"from_erc20\" or \"from_bank_denom\" must be set") + }) +} + +func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { + deps := evmtest.NewTestDeps() + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + s.T().Log("Deploy ERC20") + metadata := keeper.ERC20Metadata{ + Name: "erc20name", + Symbol: "TOKEN", + Decimals: 18, + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_ERC20Minter, + metadata.Name, metadata.Symbol, metadata.Decimals, + ) + s.Require().NoError(err) + + s.T().Log("CreateFunToken for the ERC20") + resp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: ð.EIP55Addr{ + Address: deployResp.ContractAddr, + }, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err, "erc20 %s", deployResp.ContractAddr) + bankDemon := resp.FuntokenMapping.BankDenom + + s.Run("happy: mint erc20 tokens", func() { + contractInput, err := embeds.SmartContract_ERC20Minter.ABI.Pack("mint", deps.Sender.EthAddr, big.NewInt(69_420)) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &deployResp.ContractAddr, /*to*/ + true, /*commit*/ + contractInput, + keeper.Erc20GasLimitExecute, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + }) + + randomAcc := testutil.AccAddress() + s.Run("happy: send erc20 tokens to Bank", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack("sendToBank", deployResp.ContractAddr, big.NewInt(1), randomAcc.String()) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &precompile.PrecompileAddr_FunToken, /*to*/ + true, /*commit*/ + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + }) + + s.Run("happy: check balances", func() { + evmObj, _ := deps.NewEVM() + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(69_419), "expect nonzero balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, evm.EVM_MODULE_ADDRESS, big.NewInt(1), "expect nonzero balance") + s.Require().Equal(sdk.NewInt(1), + deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount, + ) + }) + + s.Run("sad: send too many erc20 tokens to Bank", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack("sendToBank", deployResp.ContractAddr, big.NewInt(70_000), randomAcc.String()) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &precompile.PrecompileAddr_FunToken, /*to*/ + true, /*commit*/ + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().Error(err, evmResp.String()) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + }) + + s.Run("happy: send Bank tokens back to erc20", func() { + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + ToEthAddr: eth.EIP55Addr{ + Address: deps.Sender.EthAddr, + }, + Sender: randomAcc.String(), + BankCoin: sdk.NewCoin(bankDemon, sdk.NewInt(1)), + }, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + }) + + s.T().Log("check balances") + s.Run("happy: check balances", func() { + evmObj, _ := deps.NewEVM() + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(69_420), "expect nonzero balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect nonzero balance") + s.Require().True( + deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount.Equal(sdk.NewInt(0)), + ) + }) + + s.T().Log("sad: send too many Bank tokens back to erc20") + _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + ToEthAddr: eth.EIP55Addr{ + Address: deps.Sender.EthAddr, + }, + Sender: randomAcc.String(), + BankCoin: sdk.NewCoin(bankDemon, sdk.NewInt(1)), + }, + ) + s.Require().Error(err) +} + +// TestCreateFunTokenFromERC20MaliciousName tries to create funtoken from a contract +// with a malicious (gas intensive) name() function. +// Fun token should fail creation with "out of gas" +func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20MaliciousName() { + deps := evmtest.NewTestDeps() + + s.T().Log("Deploy ERC20MaliciousName") + metadata := keeper.ERC20Metadata{ + Name: "erc20name", + Symbol: "TOKEN", + Decimals: 18, + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestERC20MaliciousName, + metadata.Name, metadata.Symbol, metadata.Decimals, + ) + s.Require().NoError(err) + + erc20Addr := eth.EIP55Addr{ + Address: deployResp.ContractAddr, + } + + s.T().Log("sad: CreateFunToken for ERC20 with malicious name") + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: &erc20Addr, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().ErrorContains(err, "gas required exceeds allowance") +} + +// TestFunTokenFromERC20MaliciousTransfer creates a funtoken from a contract +// with a malicious (gas intensive) transfer() function. +// Fun token should be created but sending from erc20 to bank should fail with out of gas +func (s *FunTokenFromErc20Suite) TestFunTokenFromERC20MaliciousTransfer() { + deps := evmtest.NewTestDeps() + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + s.T().Log("Deploy ERC20MaliciousTransfer") + metadata := keeper.ERC20Metadata{ + Name: "erc20name", + Symbol: "TOKEN", + Decimals: 18, + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestERC20MaliciousTransfer, + metadata.Name, metadata.Symbol, metadata.Decimals, + ) + s.Require().NoError(err) + + erc20Addr := eth.EIP55Addr{ + Address: deployResp.ContractAddr, + } + + s.T().Log("happy: CreateFunToken for ERC20 with malicious transfer") + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: &erc20Addr, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + randomAcc := testutil.AccAddress() + + s.T().Log("send erc20 tokens to cosmos") + input, err := embeds.SmartContract_FunToken.ABI.Pack("sendToBank", deployResp.ContractAddr, big.NewInt(1), randomAcc.String()) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + evm.EVM_MODULE_ADDRESS, + &precompile.PrecompileAddr_FunToken, + true, + input, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().ErrorContains(err, "gas required exceeds allowance") + s.Require().NotZero(evmResp.GasUsed) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) +} + +// TestFunTokenInfiniteRecursionERC20 creates a funtoken from a contract +// with a malicious recursive balanceOf() and transfer() functions. +func (s *FunTokenFromErc20Suite) TestFunTokenInfiniteRecursionERC20() { + deps := evmtest.NewTestDeps() + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + s.T().Log("Deploy InfiniteRecursionERC20") + metadata := keeper.ERC20Metadata{ + Name: "erc20name", + Symbol: "TOKEN", + Decimals: 18, + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestInfiniteRecursionERC20, + metadata.Name, metadata.Symbol, metadata.Decimals, + ) + s.Require().NoError(err) + + erc20Addr := eth.EIP55Addr{ + Address: deployResp.ContractAddr, + } + + s.T().Log("happy: CreateFunToken for ERC20 with infinite recursion") + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: &erc20Addr, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + + s.T().Log("happy: call attackBalance()") + contractInput, err := embeds.SmartContract_TestInfiniteRecursionERC20.ABI.Pack("attackBalance") + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &erc20Addr.Address, /*to*/ + false, /*commit*/ + contractInput, + 10_000_000, + ) + s.Require().NoError(err) + s.Require().NotZero(evmResp.GasUsed) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + + s.T().Log("sad: call attackTransfer()") + contractInput, err = embeds.SmartContract_TestInfiniteRecursionERC20.ABI.Pack("attackTransfer") + s.Require().NoError(err) + evmObj, _ = deps.NewEVM() + evmResp, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &erc20Addr.Address, /*to*/ + true, /*commit*/ + contractInput, + 10_000_000, + ) + s.Require().ErrorContains(err, "execution reverted") + s.Require().NotZero(evmResp.GasUsed) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) +} + +// TestSendERC20WithFee creates a funtoken from a malicious contract which charges a 10% fee on any transfer. +// Test ensures that after sending ERC20 token to coin and back, all bank coins are burned. +func (s *FunTokenFromErc20Suite) TestSendERC20WithFee() { + deps := evmtest.NewTestDeps() + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + s.T().Log("Deploy ERC20") + metadata := keeper.ERC20Metadata{ + Name: "erc20name", + Symbol: "TOKEN", + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestERC20TransferWithFee, + metadata.Name, metadata.Symbol, + ) + s.Require().NoError(err) + + s.T().Log("CreateFunToken for the ERC20 with fee") + resp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: ð.EIP55Addr{ + Address: deployResp.ContractAddr, + }, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err, "erc20 %s", deployResp.ContractAddr) + bankDemon := resp.FuntokenMapping.BankDenom + + randomAcc := testutil.AccAddress() + + s.T().Log("send erc20 tokens to Bank") + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( + "sendToBank", + deployResp.ContractAddr, /*erc20Addr*/ + big.NewInt(100), /*amount*/ + randomAcc.String(), /*to*/ + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, /*from*/ + &precompile.PrecompileAddr_FunToken, /*to*/ + true, /*commit*/ + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + + s.T().Log("check balances") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(900), "expect 900 balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deployResp.ContractAddr, big.NewInt(10), "expect 10 balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, evm.EVM_MODULE_ADDRESS, big.NewInt(90), "expect 90 balance") + + s.Require().Equal(sdk.NewInt(90), deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount) + + s.T().Log("send Bank tokens back to erc20") + _, err = deps.EvmKeeper.ConvertCoinToEvm(sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + ToEthAddr: eth.EIP55Addr{ + Address: deps.Sender.EthAddr, + }, + Sender: randomAcc.String(), + BankCoin: sdk.NewCoin(bankDemon, sdk.NewInt(90)), + }, + ) + s.Require().NoError(err) + + s.T().Log("check balances") + evmObj, _ = deps.NewEVM() + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deps.Sender.EthAddr, big.NewInt(981), "expect 981 balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, deployResp.ContractAddr, big.NewInt(19), "expect 19 balance") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, deployResp.ContractAddr, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect 0 balance") + s.Require().True(deps.App.BankKeeper.GetBalance(deps.Ctx, randomAcc, bankDemon).Amount.Equal(sdk.NewInt(0))) + s.Require().True(deps.App.BankKeeper.GetBalance(deps.Ctx, evm.EVM_MODULE_ADDRESS_NIBI, bankDemon).Amount.Equal(sdk.NewInt(0))) +} + +type MkrMetadata struct { + Symbol [32]byte +} + +func (s *FunTokenFromErc20Suite) TestFindMKRMetadata() { + deps := evmtest.NewTestDeps() + + s.T().Log("Deploy MKR") + + byteSlice, err := hex.DecodeString("4d4b520000000000000000000000000000000000000000000000000000000000") + s.Require().NoError(err) + var byteArray [32]byte + copy(byteArray[:], byteSlice) + + metadata := MkrMetadata{ + Symbol: byteArray, + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestBytes32Metadata, + metadata.Symbol, + ) + s.Require().NoError(err) + + s.T().Log("set name") + + byteSlice, err = hex.DecodeString("4d616b6572000000000000000000000000000000000000000000000000000000") + s.Require().NoError(err) + copy(byteArray[:], byteSlice) + + contractInput, err := embeds.SmartContract_TestBytes32Metadata.ABI.Pack( + "setName", + byteArray, + ) + s.Require().NoError(err) + + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &deployResp.ContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greater(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed) + + info, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, evmObj, deployResp.ContractAddr, embeds.SmartContract_TestBytes32Metadata.ABI) + s.Require().NoError(err) + + actualMetadata := keeper.ERC20Metadata{ + Name: "Maker", + Symbol: "MKR", + Decimals: 18, + } + s.Require().Equal(actualMetadata, *info) +} + +type FunTokenFromErc20Suite struct { + suite.Suite +} + +func TestFunTokenFromErc20Suite(t *testing.T) { + suite.Run(t, new(FunTokenFromErc20Suite)) +} diff --git a/x/evm/keeper/funtoken_state.go b/x/evm/keeper/funtoken_state.go new file mode 100644 index 000000000..c8f36fa07 --- /dev/null +++ b/x/evm/keeper/funtoken_state.go @@ -0,0 +1,83 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "github.com/NibiruChain/collections" + sdkcodec "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// FunTokenState isolates the key-value stores (collections) for fungible token +// mappings. This struct is written as an extension of the default indexed map to +// add utility functions. +type FunTokenState struct { + collections.IndexedMap[[]byte, evm.FunToken, IndexesFunToken] +} + +func NewFunTokenState( + cdc sdkcodec.BinaryCodec, + storeKey storetypes.StoreKey, +) FunTokenState { + primaryKeyEncoder := eth.KeyEncoderBytes + valueEncoder := collections.ProtoValueEncoder[evm.FunToken](cdc) + idxMap := collections.NewIndexedMap( + storeKey, evm.KeyPrefixFunTokens, primaryKeyEncoder, valueEncoder, + IndexesFunToken{ + ERC20Addr: collections.NewMultiIndex( + storeKey, evm.KeyPrefixFunTokenIdxErc20, + eth.KeyEncoderEthAddr, // indexing key (IK): ERC-20 addr + primaryKeyEncoder, + func(v evm.FunToken) gethcommon.Address { + return v.Erc20Addr.Address + }, + ), + BankDenom: collections.NewMultiIndex( + storeKey, evm.KeyPrefixFunTokenIdxBankDenom, + collections.StringKeyEncoder, // indexing key (IK): Coin denom + primaryKeyEncoder, + func(v evm.FunToken) string { return v.BankDenom }, + ), + }, + ) + return FunTokenState{IndexedMap: idxMap} +} + +func (idxs IndexesFunToken) IndexerList() []collections.Indexer[[]byte, evm.FunToken] { + return []collections.Indexer[[]byte, evm.FunToken]{ + idxs.ERC20Addr, + idxs.BankDenom, + } +} + +// IndexesFunToken: Abstraction for indexing over the FunToken store. +type IndexesFunToken struct { + // ERC20Addr (MultiIndex): Index FunToken by ERC-20 contract address. + // - indexing key (IK): ERC-20 addr + // - primary key (PK): FunToken ID + // - value (V): FunToken value + ERC20Addr collections.MultiIndex[gethcommon.Address, []byte, evm.FunToken] + + // BankDenom (MultiIndex): Index FunToken by coin denomination + // - indexing key (IK): Coin denom + // - primary key (PK): FunToken ID + // - value (V): FunToken value + BankDenom collections.MultiIndex[string, []byte, evm.FunToken] +} + +// Insert adds an [evm.FunToken] to state with defensive validation. Errors if +// the given inputs would result in a corrupted [evm.FunToken]. +func (fun FunTokenState) SafeInsert( + ctx sdk.Context, erc20 gethcommon.Address, bankDenom string, isMadeFromCoin bool, +) error { + funtoken := evm.NewFunToken(erc20, bankDenom, isMadeFromCoin) + if err := funtoken.Validate(); err != nil { + return err + } + fun.Insert(ctx, funtoken.ID(), funtoken) + return nil +} diff --git a/x/evm/keeper/funtoken_state_test.go b/x/evm/keeper/funtoken_state_test.go new file mode 100644 index 000000000..32ba7d3ed --- /dev/null +++ b/x/evm/keeper/funtoken_state_test.go @@ -0,0 +1,81 @@ +package keeper_test + +import ( + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *Suite) TestInsertAndGet() { + deps := evmtest.NewTestDeps() + + erc20Addr := gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477") + err := deps.EvmKeeper.FunTokens.SafeInsert( + deps.Ctx, + erc20Addr, + "unibi", + true, + ) + s.Require().NoError(err) + + // test Get + funToken, err := deps.EvmKeeper.FunTokens.Get(deps.Ctx, evm.NewFunTokenID(erc20Addr, "unibi")) + s.Require().NoError(err) + s.Require().Equal(gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"), funToken.Erc20Addr.Address) + s.Require().Equal("unibi", funToken.BankDenom) + s.Require().True(funToken.IsMadeFromCoin) + + // iter := deps.K.FunTokens.Indexes.BankDenom.ExactMatch(deps.Ctx, "unibi") + // deps.K.FunTokens.Collect(ctx) +} + +func (s *Suite) TestCollect() { + deps := evmtest.NewTestDeps() + + erc20Addr := gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477") + err := deps.EvmKeeper.FunTokens.SafeInsert( + deps.Ctx, + erc20Addr, + "unibi", + true, + ) + s.Require().NoError(err) + + // test Collect by bank denom + iter := deps.EvmKeeper.FunTokens.Indexes.BankDenom.ExactMatch(deps.Ctx, "unibi") + funTokens := deps.EvmKeeper.FunTokens.Collect(deps.Ctx, iter) + s.Require().Len(funTokens, 1) + s.Require().Equal(gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"), funTokens[0].Erc20Addr.Address) + s.Require().Equal("unibi", funTokens[0].BankDenom) + s.Require().True(funTokens[0].IsMadeFromCoin) + + // test Collect by erc20 addr + iter2 := deps.EvmKeeper.FunTokens.Indexes.ERC20Addr.ExactMatch(deps.Ctx, erc20Addr) + funTokens = deps.EvmKeeper.FunTokens.Collect(deps.Ctx, iter2) + s.Require().Len(funTokens, 1) + s.Require().Equal(gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"), funTokens[0].Erc20Addr.Address) + s.Require().Equal("unibi", funTokens[0].BankDenom) + s.Require().True(funTokens[0].IsMadeFromCoin) +} + +func (s *Suite) TestDelete() { + deps := evmtest.NewTestDeps() + + erc20Addr := gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477") + err := deps.EvmKeeper.FunTokens.SafeInsert( + deps.Ctx, + erc20Addr, + "unibi", + true, + ) + s.Require().NoError(err) + + // test Delete + err = deps.EvmKeeper.FunTokens.Delete(deps.Ctx, evm.NewFunTokenID(erc20Addr, "unibi")) + s.Require().NoError(err) + + // test Get + _, err = deps.EvmKeeper.FunTokens.Get(deps.Ctx, evm.NewFunTokenID(erc20Addr, "unibi")) + s.Require().Error(err) +} diff --git a/x/evm/keeper/gas_fees.go b/x/evm/keeper/gas_fees.go new file mode 100644 index 000000000..8b8a6a495 --- /dev/null +++ b/x/evm/keeper/gas_fees.go @@ -0,0 +1,196 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "math/big" + + "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// GetEthIntrinsicGas returns the intrinsic gas cost for the transaction +func (k *Keeper) GetEthIntrinsicGas( + ctx sdk.Context, + msg core.Message, + cfg *params.ChainConfig, + isContractCreation bool, +) (uint64, error) { + return core.IntrinsicGas( + msg.Data(), msg.AccessList(), + isContractCreation, true, true, + ) +} + +// RefundGas transfers the leftover gas to the sender of the message. +func (k *Keeper) RefundGas( + ctx sdk.Context, + msgFrom gethcommon.Address, + leftoverGas uint64, + weiPerGas *big.Int, +) error { + // Return EVM tokens for remaining gas, exchanged at the original rate. + leftoverWei := new(big.Int).Mul( + new(big.Int).SetUint64(leftoverGas), + weiPerGas, + ) + leftoverMicronibi := evm.WeiToNative(leftoverWei) + + switch leftoverMicronibi.Sign() { + case -1: + // Should be impossible since leftoverGas is a uint64. Reaching this case + // would imply a critical error in the effective gas calculation. + return errors.Wrapf(evm.ErrInvalidRefund, + "refunded amount value cannot be negative %s", leftoverMicronibi, + ) + case 1: + refundedCoins := sdk.Coins{sdk.NewCoin(evm.EVMBankDenom, sdkmath.NewIntFromBigInt(leftoverMicronibi))} + + // Refund to sender from the fee collector module account. This account + // manages the collection of gas fees. + err := k.Bank.SendCoinsFromModuleToAccount( + ctx, + authtypes.FeeCollectorName, // sender + msgFrom.Bytes(), // recipient + refundedCoins, + ) + if err != nil { + err = errors.Wrapf(errortypes.ErrInsufficientFunds, "fee collector account failed to refund fees: %s", err.Error()) + return errors.Wrapf(err, "failed to refund %d leftover gas (%s)", leftoverGas, refundedCoins.String()) + } + default: + // no refund + } + + return nil +} + +// gasToRefund calculates the amount of gas the state machine should refund to +// the sender. +// EIP-3529: refunds are capped to gasUsed / 5 +func gasToRefund(availableRefundAmount, gasUsed uint64) uint64 { + refundAmount := gasUsed / params.RefundQuotientEIP3529 + if refundAmount > availableRefundAmount { + // Apply refundAmount counter + return availableRefundAmount + } + return refundAmount +} + +// CheckSenderBalance validates that the tx cost value is positive and that the +// sender has enough funds to pay for the fees and value of the transaction. +func CheckSenderBalance( + balanceWei *big.Int, + txData evm.TxData, +) error { + cost := txData.Cost() + + if cost.Sign() < 0 { + return errors.Wrapf( + errortypes.ErrInvalidCoins, + "tx cost (%s) is negative and invalid", cost, + ) + } + + if balanceWei.Cmp(big.NewInt(0)) < 0 || balanceWei.Cmp(cost) < 0 { + return errors.Wrapf( + errortypes.ErrInsufficientFunds, + "sender balance < tx cost (%s < %s)", balanceWei, cost, + ) + } + return nil +} + +// DeductTxCostsFromUserBalance deducts the fees from the user balance. Returns +// an error if the specified sender address does not exist or the account balance +// is not sufficient. +func (k *Keeper) DeductTxCostsFromUserBalance( + ctx sdk.Context, + fees sdk.Coins, + from gethcommon.Address, +) error { + // fetch sender account + signerAcc, err := authante.GetSignerAcc(ctx, k.accountKeeper, from.Bytes()) + if err != nil { + return errors.Wrapf(err, "account not found for sender %s", from) + } + + // deduct the full gas cost from the user balance + if err := authante.DeductFees(k.Bank, ctx, signerAcc, fees); err != nil { + return errors.Wrapf(err, "failed to deduct full gas cost %s from the user %s balance", fees, from) + } + + return nil +} + +// VerifyFee is used to return the fee, or token payment, for the given +// transaction data in [sdk.Coin]s. It checks that the the gas limit and uses the +// "effective fee" from the [evm.TxData]. +// +// - For [evm.DynamicFeeTx], the effective gas price is the minimum of +// (baseFee + tipCap) and the gas fee cap (feeCap). +// - For [evm.LegacyTx] and [evm.AccessListTx], the effective gas price is the +// max of the gas price and baseFee. +// +// Transactions where the baseFee exceeds the feeCap are priced out +// under EIP-1559 and will not pass validation. +// +// Args: +// - txData: Tx data related to gas, effectie gas, nonce, and chain ID +// implemented by every Ethereum tx type. +// - baseFeeMicronibi:EIP1559 base fee in units of micronibi ("unibi"). +// - isCheckTx: Comes from `[sdk.Context].isCheckTx()` +func VerifyFee( + txData evm.TxData, + baseFeeMicronibi *big.Int, + isCheckTx bool, +) (sdk.Coins, error) { + isContractCreation := txData.GetTo() == nil + + gasLimit := txData.GetGas() + + var accessList gethcore.AccessList + if txData.GetAccessList() != nil { + accessList = txData.GetAccessList() + } + + intrinsicGas, err := core.IntrinsicGas(txData.GetData(), accessList, isContractCreation, true, true) + if err != nil { + return nil, errors.Wrapf( + err, + "failed to retrieve intrinsic gas, contract creation = %t", + isContractCreation, + ) + } + + // intrinsic gas verification during CheckTx + if isCheckTx && gasLimit < intrinsicGas { + return nil, errors.Wrapf( + errortypes.ErrOutOfGas, + "gas limit too low: %d (gas limit) < %d (intrinsic gas)", gasLimit, intrinsicGas, + ) + } + + if baseFeeMicronibi == nil { + baseFeeMicronibi = evm.BASE_FEE_MICRONIBI + } + + baseFeeWei := evm.NativeToWei(baseFeeMicronibi) + feeAmtMicronibi := evm.WeiToNative(txData.EffectiveFeeWei(baseFeeWei)) + bankDenom := evm.EVMBankDenom + if feeAmtMicronibi.Sign() == 0 { + // zero fee, no need to deduct + return sdk.Coins{{Denom: bankDenom, Amount: sdkmath.ZeroInt()}}, nil + } + + return sdk.Coins{{Denom: bankDenom, Amount: sdkmath.NewIntFromBigInt(feeAmtMicronibi)}}, nil +} diff --git a/x/evm/keeper/gas_fees_test.go b/x/evm/keeper/gas_fees_test.go new file mode 100644 index 000000000..0ee23b00c --- /dev/null +++ b/x/evm/keeper/gas_fees_test.go @@ -0,0 +1,256 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethparams "github.com/ethereum/go-ethereum/params" + + "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" +) + +// TestVerifyFee asserts that the result of VerifyFee is the effective fee +// in units of micronibi per gas. +func (s *Suite) TestVerifyFee() { + baseFeeMicronibi := evm.BASE_FEE_MICRONIBI + + type testCase struct { + name string + txData evm.TxData + baseFeeMicronibi *big.Int + wantCoinAmt string + wantErr string + } + + for _, getTestCase := range []func() testCase{ + func() testCase { + txData := evmtest.ValidLegacyTx() + effectiveFeeMicronibi := evm.WeiToNative(txData.EffectiveFeeWei(nil)) + return testCase{ + name: "happy: legacy tx", + txData: txData, + baseFeeMicronibi: baseFeeMicronibi, + wantCoinAmt: effectiveFeeMicronibi.String(), + wantErr: "", + } + }, + func() testCase { + txData := evmtest.ValidLegacyTx() + txData.GasLimit = gethparams.TxGas - 1 + effectiveFeeMicronibi := evm.WeiToNative(txData.EffectiveFeeWei(nil)) + return testCase{ + name: "sad: gas limit lower than global tx gas cost", + txData: txData, + baseFeeMicronibi: baseFeeMicronibi, + wantCoinAmt: effectiveFeeMicronibi.String(), + wantErr: "gas limit too low", + } + }, + func() testCase { + txData := evmtest.ValidLegacyTx() + + // Set a gas price that would make the gas fee cap "too low", i.e. + // lower than the base fee + baseFeeWei := evm.NativeToWei(baseFeeMicronibi) + lowGasPrice := sdkmath.NewIntFromBigInt( + new(big.Int).Sub(baseFeeWei, big.NewInt(1)), + ) + txData.GasPrice = &lowGasPrice + + effectiveFeeMicronibi := evm.WeiToNative(txData.EffectiveFeeWei(baseFeeWei)) + + return testCase{ + name: "happy: gas fee cap lower than base fee", + txData: txData, + baseFeeMicronibi: baseFeeMicronibi, + wantCoinAmt: effectiveFeeMicronibi.String(), + wantErr: "", + } + }, + func() testCase { + txData := evmtest.ValidLegacyTx() + + // Set the base fee per gas and user-configured fee per gas to 0. + gasPrice := sdkmath.ZeroInt() + txData.GasLimit = gethparams.TxGas // needed for intrinsic gas + txData.GasPrice = &gasPrice + baseFeeMicronibi := big.NewInt(0) + + // Expect a cost to be 0 + wantCoinAmt := "0" + effectiveFeeMicronibi := evm.WeiToNative(txData.EffectiveFeeWei(nil)) + s.Require().Equal(wantCoinAmt, effectiveFeeMicronibi.String()) + + return testCase{ + // This is impossible because base fee is 1 unibi, however this + // case is technically valid. + name: "happy: the impossible zero case", + txData: txData, + baseFeeMicronibi: baseFeeMicronibi, + wantCoinAmt: "0", + wantErr: "", + } + }, + } { + isCheckTx := true + tc := getTestCase() + s.Run(tc.name, func() { + gotCoins, err := evmkeeper.VerifyFee( + tc.txData, tc.baseFeeMicronibi, isCheckTx, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + s.Equal(tc.wantCoinAmt, gotCoins.AmountOf(evm.EVMBankDenom).String()) + }) + } +} + +// TestRefundGas: Verifies that `Keeper.RefundGas` refunds properly with +// different values of effective gas price (weiPerGas) and fee collector balances. +func (s *Suite) TestRefundGas() { + type testCase struct { + name string + msgFrom gethcommon.Address + leftoverGas uint64 + // Comes from EffectiveGasPriceWeiPerGas + weiPerGas *big.Int + // (Optional) Expected error message that occurs from RefundGas + wantErr string + // refund amount is leftoverGas * weiPerGas * micronibiPerWei + wantRefundAmt *big.Int + } + + feeCollectorInitialBalance := big.NewInt(40_000) + fundFeeCollectorEvmBal := func(deps *evmtest.TestDeps, s *Suite, bal *big.Int) { + err := testapp.FundModuleAccount( + deps.App.BankKeeper, deps.Ctx, auth.FeeCollectorName, + sdk.NewCoins(sdk.NewCoin( + evm.EVMBankDenom, math.NewIntFromBigInt(bal), + )), + ) + s.Require().NoError(err) + } + + for _, getTestCase := range []func(deps *evmtest.TestDeps) testCase{ + func(deps *evmtest.TestDeps) testCase { + fundFeeCollectorEvmBal(deps, s, feeCollectorInitialBalance) + return testCase{ + name: "happy: geth tx gas, base fee normal", + msgFrom: deps.Sender.EthAddr, + leftoverGas: gethparams.TxGas, + weiPerGas: evm.BASE_FEE_WEI, + wantErr: "", + wantRefundAmt: new(big.Int).SetUint64(gethparams.TxGas), + } + }, + func(deps *evmtest.TestDeps) testCase { + fundFeeCollectorEvmBal(deps, s, feeCollectorInitialBalance) + return testCase{ + name: "happy: minimum wei per gas -> 0 refund", + msgFrom: deps.Sender.EthAddr, + leftoverGas: gethparams.TxGas, + weiPerGas: big.NewInt(1), + wantErr: "", + wantRefundAmt: new(big.Int).SetUint64(0), + } + }, + func(deps *evmtest.TestDeps) testCase { + fundFeeCollectorEvmBal(deps, s, feeCollectorInitialBalance) + return testCase{ + name: "happy: wei per gas slightly below default base fee", + msgFrom: deps.Sender.EthAddr, + leftoverGas: gethparams.TxGas, + weiPerGas: new(big.Int).Sub(evm.BASE_FEE_WEI, big.NewInt(1)), + wantErr: "", + wantRefundAmt: new(big.Int).SetUint64(20_999), + } + }, + func(deps *evmtest.TestDeps) testCase { + fundFeeCollectorEvmBal(deps, s, feeCollectorInitialBalance) + return testCase{ + name: "happy: wei per gas 10% of default base fee", + msgFrom: deps.Sender.EthAddr, + leftoverGas: gethparams.TxGas, + weiPerGas: new(big.Int).Quo(evm.BASE_FEE_WEI, big.NewInt(10)), + wantErr: "", + wantRefundAmt: new(big.Int).SetUint64(2100), + } + }, + func(deps *evmtest.TestDeps) testCase { + // fundFeeCollectorEvmBal(deps, s, feeCollectorInitialBalance) + return testCase{ + name: "sad: geth tx gas, base fee normal, fee collector is broke", + msgFrom: deps.Sender.EthAddr, + leftoverGas: gethparams.TxGas, + weiPerGas: evm.BASE_FEE_WEI, + wantErr: "failed to refund 21000 leftover gas (21000unibi)", + } + }, + func(deps *evmtest.TestDeps) testCase { + fundFeeCollectorEvmBal(deps, s, feeCollectorInitialBalance) + return testCase{ + name: "sad: geth tx gas, negative base fee (impossible but here for completeness", + msgFrom: deps.Sender.EthAddr, + leftoverGas: gethparams.TxGas, + weiPerGas: new(big.Int).Neg(evm.BASE_FEE_WEI), + wantErr: evm.ErrInvalidRefund.Error(), + } + }, + } { + deps := evmtest.NewTestDeps() + tc := getTestCase(&deps) + s.Run(tc.name, func() { + fromBalBefore := deps.App.BankKeeper.GetBalance( + deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom, + ).Amount.BigInt() + feeCollectorBalBefore := deps.App.BankKeeper.GetBalance( + deps.Ctx, + auth.NewModuleAddress(auth.FeeCollectorName), + evm.EVMBankDenom, + ).Amount.BigInt() + + err := deps.EvmKeeper.RefundGas( + deps.Ctx, tc.msgFrom, tc.leftoverGas, tc.weiPerGas, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + + // refund amount is leftoverGas * weiPerGas * micronibiPerWei + // msgFrom should have balance + fromBalAfter := deps.App.BankKeeper.GetBalance( + deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom, + ).Amount.BigInt() + feeCollectorBalAfter := deps.App.BankKeeper.GetBalance( + deps.Ctx, + auth.NewModuleAddress(auth.FeeCollectorName), + evm.EVMBankDenom, + ).Amount.BigInt() + + s.Equal( + new(big.Int).Sub(fromBalAfter, fromBalBefore).String(), + tc.wantRefundAmt.String(), + "sender balance did not get refunded as expected", + ) + s.Equal( + new(big.Int).Sub(feeCollectorBalAfter, feeCollectorBalBefore).String(), + new(big.Int).Neg(tc.wantRefundAmt).String(), + "fee collector did not refund as expected", + ) + }) + } +} diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go new file mode 100644 index 000000000..da2274d50 --- /dev/null +++ b/x/evm/keeper/grpc_query.go @@ -0,0 +1,832 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "math/big" + "time" + + grpccodes "google.golang.org/grpc/codes" + grpcstatus "google.golang.org/grpc/status" + + sdkmath "cosmossdk.io/math" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/logger" + gethparams "github.com/ethereum/go-ethereum/params" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" +) + +// Compile-time interface assertion +var _ evm.QueryServer = &Keeper{} + +// EthAccount: Implements the gRPC query for "/eth.evm.v1.Query/EthAccount". +// EthAccount retrieves the account and balance details for an account with the +// given address. +// +// Parameters: +// - goCtx: The context.Context object representing the request context. +// - req: Request containing the address in either Ethereum hexadecimal or +// Bech32 format. +func (k Keeper) EthAccount( + goCtx context.Context, req *evm.QueryEthAccountRequest, +) (*evm.QueryEthAccountResponse, error) { + isBech32, err := req.Validate() + if err != nil { + return nil, err + } + + var addrEth gethcommon.Address + var addrBech32 sdk.AccAddress + + if isBech32 { + addrBech32 = sdk.MustAccAddressFromBech32(req.Address) + addrEth = eth.NibiruAddrToEthAddr(addrBech32) + } else { + addrEth = gethcommon.HexToAddress(req.Address) + addrBech32 = eth.EthAddrToNibiruAddr(addrEth) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + acct := k.getAccountWithoutBalance(ctx, addrEth) + if acct == nil { + return nil, fmt.Errorf("account not found for %s", addrEth.Hex()) + } + balNative := k.Bank.GetBalance(ctx, addrBech32, evm.EVMBankDenom).Amount.BigInt() + + return &evm.QueryEthAccountResponse{ + EthAddress: addrEth.Hex(), + Bech32Address: addrBech32.String(), + Balance: balNative.String(), + BalanceWei: evm.NativeToWei(balNative).String(), + CodeHash: gethcommon.BytesToHash(acct.CodeHash).Hex(), + Nonce: acct.Nonce, + }, nil +} + +// ValidatorAccount: Implements the gRPC query for +// "/eth.evm.v1.Query/ValidatorAccount". ValidatorAccount retrieves the account +// details for a given validator consensus address. +// +// Parameters: +// - goCtx: The context.Context object representing the request context. +// - req: Request containing the validator consensus address. +// +// Returns: +// - Response containing the account details. +// - An error if the account retrieval process encounters any issues. +func (k Keeper) ValidatorAccount( + goCtx context.Context, req *evm.QueryValidatorAccountRequest, +) (*evm.QueryValidatorAccountResponse, error) { + consAddr, err := req.Validate() + if err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr) + if !found { + return nil, fmt.Errorf("validator not found for %s", consAddr.String()) + } + + nibiruAddr := sdk.AccAddress(validator.GetOperator()) + res := evm.QueryValidatorAccountResponse{ + AccountAddress: nibiruAddr.String(), + } + + account := k.accountKeeper.GetAccount(ctx, nibiruAddr) + if account != nil { + res.Sequence = account.GetSequence() + res.AccountNumber = account.GetAccountNumber() + } + + return &res, nil +} + +// Balance: Implements the gRPC query for "/eth.evm.v1.Query/Balance". +// Balance retrieves the balance of an Ethereum address in "wei", the smallest +// unit of "Ether". Ether refers to NIBI tokens on Nibiru EVM. +// +// Parameters: +// - goCtx: The context.Context object representing the request context. +// - req: The QueryBalanceRequest object containing the Ethereum address. +// +// Returns: +// - A pointer to the QueryBalanceResponse object containing the balance. +// - An error if the balance retrieval process encounters any issues. +func (k Keeper) Balance(goCtx context.Context, req *evm.QueryBalanceRequest) (*evm.QueryBalanceResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + ctx := sdk.UnwrapSDKContext(goCtx) + balanceInt := k.GetEvmGasBalance(ctx, gethcommon.HexToAddress(req.Address)) + return &evm.QueryBalanceResponse{ + Balance: balanceInt.String(), + BalanceWei: evm.NativeToWei(balanceInt).String(), + }, nil +} + +// BaseFee implements the Query/BaseFee gRPC method +func (k Keeper) BaseFee( + goCtx context.Context, _ *evm.QueryBaseFeeRequest, +) (*evm.QueryBaseFeeResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + baseFeeMicronibiPerGas := sdkmath.NewIntFromBigInt(k.BaseFeeMicronibiPerGas(ctx)) + baseFeeWei := sdkmath.NewIntFromBigInt( + evm.NativeToWei(baseFeeMicronibiPerGas.BigInt()), + ) + return &evm.QueryBaseFeeResponse{ + BaseFee: &baseFeeWei, + BaseFeeUnibi: &baseFeeMicronibiPerGas, + }, nil +} + +// Storage: Implements the gRPC query for "/eth.evm.v1.Query/Storage". +// Storage retrieves the storage value for a given Ethereum address and key. +// +// Parameters: +// - goCtx: The context.Context object representing the request context. +// - req: The QueryStorageRequest object containing the Ethereum address and key. +// +// Returns: +// - A pointer to the QueryStorageResponse object containing the storage value. +// - An error if the storage retrieval process encounters any issues. +func (k Keeper) Storage( + goCtx context.Context, req *evm.QueryStorageRequest, +) (*evm.QueryStorageResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + ctx := sdk.UnwrapSDKContext(goCtx) + + address := gethcommon.HexToAddress(req.Address) + key := gethcommon.HexToHash(req.Key) + + state := k.GetState(ctx, address, key) + stateHex := state.Hex() + + return &evm.QueryStorageResponse{ + Value: stateHex, + }, nil +} + +// Code: Implements the gRPC query for "/eth.evm.v1.Query/Code". +// Code retrieves the smart contract bytecode associated with a given Ethereum +// address. +// +// Parameters: +// - goCtx: The context.Context object representing the request context. +// - req: Request with the Ethereum address of the smart contract bytecode. +// +// Returns: +// - Response containing the smart contract bytecode. +// - An error if the code retrieval process encounters any issues. +func (k Keeper) Code( + goCtx context.Context, req *evm.QueryCodeRequest, +) (*evm.QueryCodeResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + address := gethcommon.HexToAddress(req.Address) + acct := k.getAccountWithoutBalance(ctx, address) + + var code []byte + if acct != nil && acct.IsContract() { + code = k.GetCode(ctx, gethcommon.BytesToHash(acct.CodeHash)) + } + + return &evm.QueryCodeResponse{ + Code: code, + }, nil +} + +// Params: Implements the gRPC query for "/eth.evm.v1.Query/Params". +// Params retrieves the EVM module parameters. +// +// Parameters: +// - goCtx: The context.Context object representing the request context. +// - req: The QueryParamsRequest object (unused). +// +// Returns: +// - A pointer to the QueryParamsResponse object containing the EVM module parameters. +// - An error if the parameter retrieval process encounters any issues. +func (k Keeper) Params( + goCtx context.Context, _ *evm.QueryParamsRequest, +) (*evm.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + params := k.GetParams(ctx) + return &evm.QueryParamsResponse{ + Params: params, + }, nil +} + +// EthCall: Implements the gRPC query for "/eth.evm.v1.Query/EthCall". +// EthCall performs a smart contract call using the eth_call JSON-RPC method. +// +// An "eth_call" is a method from the Ethereum JSON-RPC specification that allows +// one to call a smart contract function without execution a transaction on the +// blockchain. This is useful for simulating transactions and for reading data +// from the chain using responses from smart contract calls. +// +// Parameters: +// - goCtx: Request context with information about the current block that +// serves as the main access point to the blockchain state. +// - req: "eth_call" parameters to interact with a smart contract. +// +// Returns: +// - A pointer to the MsgEthereumTxResponse object containing the result of the eth_call. +// - An error if the eth_call process encounters any issues. +func (k *Keeper) EthCall( + goCtx context.Context, req *evm.EthCallRequest, +) (*evm.MsgEthereumTxResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + var args evm.JsonTxArgs + err := json.Unmarshal(req.Args, &args) + if err != nil { + return nil, grpcstatus.Error(grpccodes.InvalidArgument, err.Error()) + } + evmCfg := k.GetEVMConfig(ctx) + + // ApplyMessageWithConfig expect correct nonce set in msg + nonce := k.GetAccNonce(ctx, args.GetFrom()) + args.Nonce = (*hexutil.Uint64)(&nonce) + + msg, err := args.ToMessage(req.GasCap, evmCfg.BaseFeeWei) + if err != nil { + return nil, grpcstatus.Error(grpccodes.InvalidArgument, err.Error()) + } + + txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash())) + + // pass false to not commit StateDB + stateDB := statedb.New(ctx, k, txConfig) + evm := k.NewEVM(ctx, msg, evmCfg, nil /*tracer*/, stateDB) + res, err := k.ApplyEvmMsg(ctx, msg, evm, nil /*tracer*/, false /*commit*/, txConfig.TxHash) + if err != nil { + return nil, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + + return res, nil +} + +// EstimateGas: Implements the gRPC query for "/eth.evm.v1.Query/EstimateGas". +// EstimateGas implements eth_estimateGas rpc api. +func (k Keeper) EstimateGas( + goCtx context.Context, req *evm.EthCallRequest, +) (*evm.EstimateGasResponse, error) { + return k.EstimateGasForEvmCallType(goCtx, req, evm.CallTypeRPC) +} + +// EstimateGasForEvmCallType estimates the gas cost of a transaction. This can be +// called with the "eth_estimateGas" JSON-RPC method or smart contract query. +// +// When [EstimateGas] is called from the JSON-RPC client, we need to reset the +// gas meter before simulating the transaction (tx) to have an accurate gas +// estimate txs using EVM extensions. +// +// Parameters: +// - goCtx: The context.Context object representing the request context. +// - req: The EthCallRequest object containing the transaction parameters. +// +// Returns: +// - A response containing the estimated gas cost. +// - An error if the gas estimation process encounters any issues. +func (k Keeper) EstimateGasForEvmCallType( + goCtx context.Context, req *evm.EthCallRequest, fromType evm.CallType, +) (*evm.EstimateGasResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + ctx := sdk.UnwrapSDKContext(goCtx) + evmCfg := k.GetEVMConfig(ctx) + + if req.GasCap < gethparams.TxGas { + return nil, grpcstatus.Errorf(grpccodes.InvalidArgument, "gas cap cannot be lower than %d", gethparams.TxGas) + } + + var args evm.JsonTxArgs + err := json.Unmarshal(req.Args, &args) + if err != nil { + return nil, grpcstatus.Error(grpccodes.InvalidArgument, err.Error()) + } + + // ApplyMessageWithConfig expect correct nonce set in msg + nonce := k.GetAccNonce(ctx, args.GetFrom()) + args.Nonce = (*hexutil.Uint64)(&nonce) + + // Binary search the gas requirement, as it may be higher than the amount used + var ( + lo = gethparams.TxGas - 1 + hi uint64 + gasCap uint64 + ) + + // Determine the highest gas limit can be used during the estimation. + if args.Gas != nil && uint64(*args.Gas) >= gethparams.TxGas { + hi = uint64(*args.Gas) + } else { + // Query block gas limit + params := ctx.ConsensusParams() + if params != nil && params.Block != nil && params.Block.MaxGas > 0 { + hi = uint64(params.Block.MaxGas) + } else { + hi = req.GasCap + } + } + + // TODO: Recap the highest gas limit with account's available balance. + // Recap the highest gas allowance with specified gascap. + if req.GasCap != 0 && hi > req.GasCap { + hi = req.GasCap + } + + gasCap = hi + + // convert the tx args to an ethereum message + evmMsg, err := args.ToMessage(req.GasCap, evmCfg.BaseFeeWei) + if err != nil { + return nil, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + + // NOTE: the errors from the executable below should be consistent with + // go-ethereum, so we don't wrap them with the gRPC status code Create a + // helper to check if a gas allowance results in an executable transaction. + executable := func(gas uint64) (vmError bool, rsp *evm.MsgEthereumTxResponse, err error) { + // update the message with the new gas value + evmMsg = gethcore.NewMessage( + evmMsg.From(), + evmMsg.To(), + evmMsg.Nonce(), + evmMsg.Value(), + gas, + evmMsg.GasPrice(), + evmMsg.GasFeeCap(), + evmMsg.GasTipCap(), + evmMsg.Data(), + evmMsg.AccessList(), + evmMsg.IsFake(), + ) + + tmpCtx := ctx + if fromType == evm.CallTypeRPC { + tmpCtx, _ = ctx.CacheContext() + + acct := k.GetAccount(tmpCtx, evmMsg.From()) + + from := evmMsg.From() + if acct == nil { + acc := k.accountKeeper.NewAccountWithAddress(tmpCtx, from[:]) + k.accountKeeper.SetAccount(tmpCtx, acc) + acct = statedb.NewEmptyAccount() + } + // When submitting a transaction, the `EthIncrementSenderSequence` ante handler increases the account nonce + acct.Nonce = nonce + 1 + err = k.SetAccount(tmpCtx, from, *acct) + if err != nil { + return true, nil, err + } + // resetting the gasMeter after increasing the sequence to have an accurate gas estimation on EVM extensions transactions + gasMeter := eth.NewInfiniteGasMeterWithLimit(evmMsg.Gas()) + tmpCtx = tmpCtx.WithGasMeter(gasMeter). + WithKVGasConfig(storetypes.GasConfig{}). + WithTransientKVGasConfig(storetypes.GasConfig{}) + } + // pass false to not commit StateDB + txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash().Bytes())) + stateDB := statedb.New(ctx, &k, txConfig) + evmObj := k.NewEVM(tmpCtx, evmMsg, evmCfg, nil /*tracer*/, stateDB) + rsp, err = k.ApplyEvmMsg(tmpCtx, evmMsg, evmObj, nil /*tracer*/, false /*commit*/, txConfig.TxHash) + if err != nil { + if errors.Is(err, core.ErrIntrinsicGas) { + return true, nil, nil // Special case, raise gas limit + } + return true, nil, fmt.Errorf("error applying EVM message to StateDB: %w", err) // Bail out + } + return len(rsp.VmError) > 0, rsp, nil + } + + // Execute the binary search and hone in on an executable gas limit + hi, err = evm.BinSearch(lo, hi, executable) + if err != nil { + return nil, err + } + + // The gas limit is now the highest gas limit that results in an executable transaction + // Reject the transaction as invalid if it still fails at the highest allowance + if hi == gasCap { + failed, result, err := executable(hi) + if err != nil { + return nil, fmt.Errorf("eth call exec error: %w", err) + } + + if failed && result != nil { + if result.VmError == vm.ErrExecutionReverted.Error() { + return nil, fmt.Errorf("Estimate gas VMError: %w", evm.NewRevertError(result.Ret)) + } + + if result.VmError == vm.ErrOutOfGas.Error() { + return nil, fmt.Errorf("gas required exceeds allowance (%d)", gasCap) + } + + return nil, fmt.Errorf("Estimate gas VMError: %s", result.VmError) + } + } + + return &evm.EstimateGasResponse{Gas: hi}, nil +} + +// TraceTx configures a new tracer according to the provided configuration, and +// executes the given message in the provided environment. The return value will +// be tracer dependent. +func (k Keeper) TraceTx( + goCtx context.Context, req *evm.QueryTraceTxRequest, +) (*evm.QueryTraceTxResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + // get the context of block beginning + contextHeight := req.BlockNumber + if contextHeight < 1 { + // 0 is a special value in `ContextWithHeight` + contextHeight = 1 + } + + ctx := sdk.UnwrapSDKContext(goCtx) + ctx = ctx.WithBlockHeight(contextHeight) + ctx = ctx.WithBlockTime(req.BlockTime) + ctx = ctx.WithHeaderHash(gethcommon.Hex2Bytes(req.BlockHash)) + + // to get the base fee we only need the block max gas in the consensus params + ctx = ctx.WithConsensusParams(&cmtproto.ConsensusParams{ + Block: &cmtproto.BlockParams{MaxGas: req.BlockMaxGas}, + }) + + evmCfg := k.GetEVMConfig(ctx) + // compute and use base fee of the height that is being traced + baseFeeWeiPerGas := k.BaseFeeWeiPerGas(ctx) + if baseFeeWeiPerGas != nil { + evmCfg.BaseFeeWei = baseFeeWeiPerGas + } + + signer := gethcore.MakeSigner(evmCfg.ChainConfig, big.NewInt(ctx.BlockHeight())) + txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash().Bytes())) + + // gas used at this point corresponds to GetProposerAddress & CalculateBaseFee + // need to reset gas meter per transaction to be consistent with tx execution + // and avoid stacking the gas used of every predecessor in the same gas meter + + for i, tx := range req.Predecessors { + ethTx := tx.AsTransaction() + msg, err := ethTx.AsMessage(signer, evmCfg.BaseFeeWei) + if err != nil { + continue + } + txConfig.TxHash = ethTx.Hash() + txConfig.TxIndex = uint(i) + // reset gas meter for each transaction + ctx = ctx.WithGasMeter(eth.NewInfiniteGasMeterWithLimit(msg.Gas())). + WithKVGasConfig(storetypes.GasConfig{}). + WithTransientKVGasConfig(storetypes.GasConfig{}) + stateDB := statedb.New(ctx, &k, txConfig) + evmObj := k.NewEVM(ctx, msg, evmCfg, nil /*tracer*/, stateDB) + rsp, err := k.ApplyEvmMsg(ctx, msg, evmObj, nil /*tracer*/, false /*commit*/, txConfig.TxHash) + if err != nil { + continue + } + txConfig.LogIndex += uint(len(rsp.Logs)) + } + + tx := req.Msg.AsTransaction() + txConfig.TxHash = tx.Hash() + if len(req.Predecessors) > 0 { + txConfig.TxIndex++ + } + + var tracerConfig json.RawMessage + if req.TraceConfig != nil && req.TraceConfig.TracerConfig != nil { + // ignore error. default to no traceConfig + tracerConfig, _ = json.Marshal(req.TraceConfig.TracerConfig) + } + + msg, err := tx.AsMessage(signer, evmCfg.BaseFeeWei) + if err != nil { + return nil, err + } + + result, _, err := k.TraceEthTxMsg(ctx, evmCfg, txConfig, msg, req.TraceConfig, tracerConfig) + if err != nil { + // error will be returned with detail status from traceTx + return nil, err + } + + resultData, err := json.Marshal(result) + if err != nil { + return nil, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + + return &evm.QueryTraceTxResponse{ + Data: resultData, + }, nil +} + +// TraceCall configures a new tracer according to the provided configuration, and +// executes the given message in the provided environment. The return value will +// be tracer dependent. +func (k Keeper) TraceCall( + goCtx context.Context, req *evm.QueryTraceTxRequest, +) (*evm.QueryTraceTxResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + // get the context of block beginning + contextHeight := req.BlockNumber + if contextHeight < 1 { + // 0 is a special value in `ContextWithHeight` + contextHeight = 1 + } + + ctx := sdk.UnwrapSDKContext(goCtx) + ctx = ctx.WithBlockHeight(contextHeight) + ctx = ctx.WithBlockTime(req.BlockTime) + ctx = ctx.WithHeaderHash(gethcommon.Hex2Bytes(req.BlockHash)) + + // to get the base fee we only need the block max gas in the consensus params + ctx = ctx.WithConsensusParams(&cmtproto.ConsensusParams{ + Block: &cmtproto.BlockParams{MaxGas: req.BlockMaxGas}, + }) + + evmCfg := k.GetEVMConfig(ctx) + + // compute and use base fee of the height that is being traced + baseFeeMicronibi := k.BaseFeeMicronibiPerGas(ctx) + if baseFeeMicronibi != nil { + evmCfg.BaseFeeWei = baseFeeMicronibi + } + + txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash().Bytes())) + + var tracerConfig json.RawMessage + if req.TraceConfig != nil && req.TraceConfig.TracerConfig != nil { + // ignore error. default to no traceConfig + tracerConfig, _ = json.Marshal(req.TraceConfig.TracerConfig) + } + + // req.Msg is not signed, so to gethcore.Message because it's not signed and will fail on getting + msgEthTx := req.Msg + txData, err := evm.UnpackTxData(req.Msg.Data) + if err != nil { + return nil, grpcstatus.Errorf(grpccodes.Internal, "failed to unpack tx data: %s", err.Error()) + } + evmMsg := gethcore.NewMessage( + gethcommon.HexToAddress(msgEthTx.From), + txData.GetTo(), + txData.GetNonce(), + txData.GetValueWei(), + txData.GetGas(), + txData.GetGasPrice(), + txData.GetGasFeeCapWei(), + txData.GetGasTipCapWei(), + txData.GetData(), + txData.GetAccessList(), + false, // isFake + ) + result, _, err := k.TraceEthTxMsg(ctx, evmCfg, txConfig, evmMsg, req.TraceConfig, tracerConfig) + if err != nil { + // error will be returned with detail status from traceTx + return nil, err + } + + resultData, err := json.Marshal(result) + if err != nil { + return nil, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + + return &evm.QueryTraceTxResponse{ + Data: resultData, + }, nil +} + +// Re-export of the default tracer timeout from go-ethereum. +// See "geth/eth/tracers/api.go". +const DefaultGethTraceTimeout = 5 * time.Second + +// TraceBlock: Implements the gRPC query for "/eth.evm.v1.Query/TraceBlock". +// Configures a Nibiru EVM tracer that is used to "trace" and analyze +// the execution of transactions within a given block. Block information is read +// from the context (goCtx). [TraceBlock] is responsible iterates over each Eth +// transaction message and calls [TraceEthTxMsg] on it. +func (k Keeper) TraceBlock( + goCtx context.Context, req *evm.QueryTraceBlockRequest, +) (*evm.QueryTraceBlockResponse, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + // get the context of block beginning + contextHeight := req.BlockNumber + if contextHeight < 1 { + // 0 is a special value in `ContextWithHeight` + contextHeight = 1 + } + + ctx := sdk.UnwrapSDKContext(goCtx). + WithBlockHeight(contextHeight). + WithBlockTime(req.BlockTime). + WithHeaderHash(gethcommon.Hex2Bytes(req.BlockHash)). + // to get the base fee we only need the block max gas in the consensus params + WithConsensusParams(&cmtproto.ConsensusParams{ + Block: &cmtproto.BlockParams{MaxGas: req.BlockMaxGas}, + }) + + evmCfg := k.GetEVMConfig(ctx) + + // compute and use base fee of height that is being traced + if baseFeeMicronibiPerGas := k.BaseFeeMicronibiPerGas(ctx); baseFeeMicronibiPerGas != nil { + baseFeeWeiPerGas := evm.NativeToWei(baseFeeMicronibiPerGas) + evmCfg.BaseFeeWei = baseFeeWeiPerGas + } + var tracerConfig json.RawMessage + if req.TraceConfig != nil && req.TraceConfig.TracerConfig != nil { + // ignore error. default to no traceConfig + tracerConfig, _ = json.Marshal(req.TraceConfig.TracerConfig) + } + + signer := gethcore.MakeSigner(evmCfg.ChainConfig, big.NewInt(ctx.BlockHeight())) + txsLength := len(req.Txs) + results := make([]*evm.TxTraceResult, 0, txsLength) + + txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash().Bytes())) + + for i, tx := range req.Txs { + result := evm.TxTraceResult{} + ethTx := tx.AsTransaction() + txConfig.TxHash = ethTx.Hash() + txConfig.TxIndex = uint(i) + msg, err := ethTx.AsMessage(signer, evmCfg.BaseFeeWei) + if err != nil { + result.Error = err.Error() + continue + } + traceResult, logIndex, err := k.TraceEthTxMsg(ctx, evmCfg, txConfig, msg, req.TraceConfig, tracerConfig) + if err != nil { + result.Error = err.Error() + } else { + txConfig.LogIndex = logIndex + result.Result = traceResult + } + results = append(results, &result) + } + + resultData, err := json.Marshal(results) + if err != nil { + return nil, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + + return &evm.QueryTraceBlockResponse{ + Data: resultData, + }, nil +} + +// TraceEthTxMsg do trace on one transaction, it returns a tuple: (traceResult, +// nextLogIndex, error). +func (k *Keeper) TraceEthTxMsg( + ctx sdk.Context, + evmCfg statedb.EVMConfig, + txConfig statedb.TxConfig, + msg gethcore.Message, + traceConfig *evm.TraceConfig, + tracerJSONConfig json.RawMessage, +) (*any, uint, error) { + // Assemble the structured logger or the JavaScript tracer + var ( + tracer tracers.Tracer + overrides *gethparams.ChainConfig + err error + timeout = DefaultGethTraceTimeout + ) + if traceConfig == nil { + traceConfig = &evm.TraceConfig{} + } + + logConfig := logger.Config{ + EnableMemory: traceConfig.EnableMemory, + DisableStorage: traceConfig.DisableStorage, + DisableStack: traceConfig.DisableStack, + EnableReturnData: traceConfig.EnableReturnData, + Debug: traceConfig.Debug, + Limit: int(traceConfig.Limit), + Overrides: overrides, + } + + tracer = logger.NewStructLogger(&logConfig) + + tCtx := &tracers.Context{ + BlockHash: txConfig.BlockHash, + TxIndex: int(txConfig.TxIndex), + TxHash: txConfig.TxHash, + } + + if traceConfig.Tracer != "" { + if tracer, err = tracers.New(traceConfig.Tracer, tCtx, tracerJSONConfig); err != nil { + return nil, 0, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + } + + // Define a meaningful timeout of a single transaction trace + if traceConfig.Timeout != "" { + if timeout, err = time.ParseDuration(traceConfig.Timeout); err != nil { + return nil, 0, grpcstatus.Errorf(grpccodes.InvalidArgument, "timeout value: %s", err.Error()) + } + } + + // Handle timeouts and RPC cancellations + deadlineCtx, cancel := context.WithTimeout(ctx.Context(), timeout) + defer cancel() + + go func() { + <-deadlineCtx.Done() + if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { + tracer.Stop(errors.New("execution timeout")) + } + }() + + // In order to be on in sync with the tx execution gas meter, + // we need to: + // 1. Reset GasMeter with InfiniteGasMeterWithLimit + // 2. Setup an empty KV gas config for gas to be calculated by opcodes + // and not kvstore actions + // 3. Setup an empty transient KV gas config for transient gas to be + // calculated by opcodes + ctx = ctx.WithGasMeter(eth.NewInfiniteGasMeterWithLimit(msg.Gas())). + WithKVGasConfig(storetypes.GasConfig{}). + WithTransientKVGasConfig(storetypes.GasConfig{}) + stateDB := statedb.New(ctx, k, txConfig) + evmObj := k.NewEVM(ctx, msg, evmCfg, tracer, stateDB) + res, err := k.ApplyEvmMsg(ctx, msg, evmObj, tracer, false /*commit*/, txConfig.TxHash) + if err != nil { + return nil, 0, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + + var result any + result, err = tracer.GetResult() + if err != nil { + return nil, 0, grpcstatus.Error(grpccodes.Internal, err.Error()) + } + + return &result, txConfig.LogIndex + uint(len(res.Logs)), nil +} + +func (k Keeper) FunTokenMapping( + goCtx context.Context, req *evm.QueryFunTokenMappingRequest, +) (*evm.QueryFunTokenMappingResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // first try lookup by cosmos denom + bankDenomIter := k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, req.Token) + funTokenMappings := k.FunTokens.Collect(ctx, bankDenomIter) + if len(funTokenMappings) > 0 { + // assumes that there is only one mapping for a given denom + return &evm.QueryFunTokenMappingResponse{ + FunToken: &funTokenMappings[0], + }, nil + } + + erc20AddrIter := k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, gethcommon.HexToAddress(req.Token)) + funTokenMappings = k.FunTokens.Collect(ctx, erc20AddrIter) + if len(funTokenMappings) > 0 { + // assumes that there is only one mapping for a given erc20 address + return &evm.QueryFunTokenMappingResponse{ + FunToken: &funTokenMappings[0], + }, nil + } + + return nil, grpcstatus.Errorf(grpccodes.NotFound, "token mapping not found for %s", req.Token) +} diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go new file mode 100644 index 000000000..2b75914b2 --- /dev/null +++ b/x/evm/keeper/grpc_query_test.go @@ -0,0 +1,1082 @@ +package keeper_test + +import ( + "encoding/json" + "fmt" + "math/big" + "strings" + + "cosmossdk.io/math" + "github.com/NibiruChain/collections" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +type TestCase[In, Out any] struct { + name string + // setup: Optional setup function to create the scenario + setup func(deps *evmtest.TestDeps) + scenario func(deps *evmtest.TestDeps) ( + req In, + wantResp Out, + ) + onTestEnd func(deps *evmtest.TestDeps) + wantErr string +} + +func InvalidEthAddr() string { return "0x0000" } + +// TraceNibiTransfer returns a hardcoded JSON string representing the expected +// trace output of a successful "ether" (unibi) token transfer. +// Used to test the correctness of "TraceTx" and "TraceBlock". +// - Note that the struct logs are empty. That is because a simple token +// transfer does not involve contract operations. +func TraceNibiTransfer() string { + return fmt.Sprintf(`{ + "gas": %d, + "failed": false, + "returnValue": "", + "structLogs": [] + }`, gethparams.TxGas) +} + +// TraceERC20Transfer returns a hardcoded JSON string representing the expected +// trace output of a successful ERC-20 token transfer (an EVM tx). +// Used to test the correctness of "TraceTx" and "TraceBlock". +func TraceERC20Transfer() string { + return `{ + "gas": 35062, + "failed": false, + "returnValue": "0000000000000000000000000000000000000000000000000000000000000001", + "structLogs": [ + { + "pc": 0, + "op": "PUSH1", + "gas": 30578, + "gasCost": 3, + "depth": 1, + "stack": [] + }` +} + +func (s *Suite) TestQueryEvmAccount() { + type In = *evm.QueryEthAccountRequest + type Out = *evm.QueryEthAccountResponse + testCases := []TestCase[In, Out]{ + { + name: "happy: fund account + query eth addr", + setup: func(deps *evmtest.TestDeps) { + // fund account with 420 tokens + ethAddr := deps.Sender.EthAddr + coins := sdk.Coins{sdk.NewInt64Coin(evm.EVMBankDenom, 420)} + err := testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, ethAddr.Bytes(), coins) + s.Require().NoError(err) + }, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryEthAccountRequest{ + Address: deps.Sender.EthAddr.Hex(), + } + wantResp = &evm.QueryEthAccountResponse{ + Balance: "420", + BalanceWei: "420" + strings.Repeat("0", 12), + CodeHash: gethcommon.BytesToHash(evm.EmptyCodeHash).Hex(), + Nonce: 0, + EthAddress: deps.Sender.EthAddr.String(), + Bech32Address: deps.Sender.NibiruAddr.String(), + } + return req, wantResp + }, + wantErr: "", + }, + { + name: "happy: fund account + query nibiru bech32 addr", + setup: func(deps *evmtest.TestDeps) { + // fund account with 420 tokens + ethAddr := deps.Sender.EthAddr + coins := sdk.Coins{sdk.NewInt64Coin(evm.EVMBankDenom, 420)} + err := testapp.FundAccount(deps.App.BankKeeper, deps.Ctx, ethAddr.Bytes(), coins) + s.Require().NoError(err) + }, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryEthAccountRequest{ + Address: deps.Sender.NibiruAddr.String(), + } + wantResp = &evm.QueryEthAccountResponse{ + Balance: "420", + BalanceWei: "420" + strings.Repeat("0", 12), + CodeHash: gethcommon.BytesToHash(evm.EmptyCodeHash).Hex(), + Nonce: 0, + EthAddress: deps.Sender.EthAddr.String(), + Bech32Address: deps.Sender.NibiruAddr.String(), + } + return req, wantResp + }, + wantErr: "", + }, + { + name: "sad: msg validation", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryEthAccountRequest{ + Address: InvalidEthAddr(), + } + wantResp = &evm.QueryEthAccountResponse{ + Balance: "0", + BalanceWei: "0", + CodeHash: gethcommon.BytesToHash(evm.EmptyCodeHash).Hex(), + Nonce: 0, + } + return req, wantResp + }, + wantErr: "not a valid ethereum hex addr", + }, + { + name: "happy: nonexistent account (hex addr input)", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + ethAcc := evmtest.NewEthPrivAcc() + req = &evm.QueryEthAccountRequest{ + Address: ethAcc.EthAddr.String(), + } + wantResp = nil + return req, wantResp + }, + wantErr: "account not found for", + }, + { + name: "happy: nonexistent account (bech32 input)", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + ethAcc := evmtest.NewEthPrivAcc() + req = &evm.QueryEthAccountRequest{ + Address: ethAcc.NibiruAddr.String(), + } + wantResp = nil + return req, wantResp + }, + wantErr: "account not found for", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.EthAccount(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.EqualValues(wantResp, gotResp) + }) + } +} + +func (s *Suite) TestQueryValidatorAccount() { + type In = *evm.QueryValidatorAccountRequest + type Out = *evm.QueryValidatorAccountResponse + testCases := []TestCase[In, Out]{ + { + name: "sad: msg validation", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryValidatorAccountRequest{ + ConsAddress: "nibi1invalidaddr", + } + wantResp = &evm.QueryValidatorAccountResponse{ + AccountAddress: sdk.AccAddress(gethcommon.Address{}.Bytes()).String(), + } + return req, wantResp + }, + wantErr: "decoding bech32 failed", + }, + { + name: "sad: validator account not found", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryValidatorAccountRequest{ + ConsAddress: "nibivalcons1ea4ef7wsatlnaj9ry3zylymxv53f9ntrjecc40", + } + wantResp = &evm.QueryValidatorAccountResponse{ + AccountAddress: sdk.AccAddress(gethcommon.Address{}.Bytes()).String(), + } + return req, wantResp + }, + wantErr: "validator not found", + }, + { + name: "happy: default values", + setup: func(deps *evmtest.TestDeps) {}, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + valopers := deps.App.StakingKeeper.GetValidators(deps.Ctx, 1) + valAddrBz := valopers[0].GetOperator().Bytes() + _, err := sdk.ConsAddressFromBech32(valopers[0].OperatorAddress) + s.ErrorContains(err, "expected nibivalcons, got nibivaloper") + consAddr := sdk.ConsAddress(valAddrBz) + + req = &evm.QueryValidatorAccountRequest{ + ConsAddress: consAddr.String(), + } + wantResp = &evm.QueryValidatorAccountResponse{ + AccountAddress: sdk.AccAddress(valAddrBz).String(), + Sequence: 0, + AccountNumber: 0, + } + return req, wantResp + }, + wantErr: "", + }, + { + name: "happy: with nonce", + setup: func(deps *evmtest.TestDeps) {}, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + valopers := deps.App.StakingKeeper.GetValidators(deps.Ctx, 1) + valAddrBz := valopers[0].GetOperator().Bytes() + consAddr := sdk.ConsAddress(valAddrBz) + + s.T().Log( + "Send coins to validator to register in the account keeper.") + coinsToSend := sdk.NewCoins(sdk.NewCoin(eth.EthBaseDenom, math.NewInt(69420))) + valAddr := sdk.AccAddress(valAddrBz) + s.NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, valAddr, + coinsToSend, + )) + + req = &evm.QueryValidatorAccountRequest{ + ConsAddress: consAddr.String(), + } + + ak := deps.App.AccountKeeper + acc := ak.GetAccount(deps.Ctx, valAddr) + s.NoError(acc.SetAccountNumber(420), "acc: ", acc.String()) + s.NoError(acc.SetSequence(69), "acc: ", acc.String()) + ak.SetAccount(deps.Ctx, acc) + + wantResp = &evm.QueryValidatorAccountResponse{ + AccountAddress: sdk.AccAddress(valAddrBz).String(), + Sequence: 69, + AccountNumber: 420, + } + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.ValidatorAccount(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.EqualValues(wantResp, gotResp) + }) + } +} + +func (s *Suite) TestQueryStorage() { + type In = *evm.QueryStorageRequest + type Out = *evm.QueryStorageResponse + testCases := []TestCase[In, Out]{ + { + name: "sad: msg validation", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryStorageRequest{ + Address: InvalidEthAddr(), + } + return req, wantResp + }, + wantErr: "InvalidArgument", + }, + { + name: "happy", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + addr := evmtest.NewEthPrivAcc().EthAddr + storageKey := gethcommon.BytesToHash([]byte("storagekey")) + req = &evm.QueryStorageRequest{ + Address: addr.Hex(), + Key: storageKey.String(), + } + + stateDB := deps.NewStateDB() + storageValue := gethcommon.BytesToHash([]byte("value")) + + stateDB.SetState(addr, storageKey, storageValue) + s.NoError(stateDB.Commit()) + + wantResp = &evm.QueryStorageResponse{ + Value: storageValue.String(), + } + return req, wantResp + }, + wantErr: "", + }, + { + name: "happy: no committed state", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + addr := evmtest.NewEthPrivAcc().EthAddr + storageKey := gethcommon.BytesToHash([]byte("storagekey")) + req = &evm.QueryStorageRequest{ + Address: addr.Hex(), + Key: storageKey.String(), + } + + wantResp = &evm.QueryStorageResponse{ + Value: gethcommon.BytesToHash([]byte{}).String(), + } + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + + gotResp, err := deps.EvmKeeper.Storage(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.EqualValues(wantResp, gotResp) + }) + } +} + +func (s *Suite) TestQueryCode() { + type In = *evm.QueryCodeRequest + type Out = *evm.QueryCodeResponse + testCases := []TestCase[In, Out]{ + { + name: "sad: msg validation", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryCodeRequest{ + Address: InvalidEthAddr(), + } + return req, wantResp + }, + wantErr: "InvalidArgument", + }, + { + name: "happy", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + addr := evmtest.NewEthPrivAcc().EthAddr + req = &evm.QueryCodeRequest{ + Address: addr.Hex(), + } + + stateDB := deps.NewStateDB() + contractBytecode := []byte("bytecode") + stateDB.SetCode(addr, contractBytecode) + s.Require().NoError(stateDB.Commit()) + + s.NotNil(stateDB.Keeper().GetAccount(deps.Ctx, addr)) + s.NotNil(stateDB.GetCode(addr)) + + wantResp = &evm.QueryCodeResponse{ + Code: contractBytecode, + } + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + + gotResp, err := deps.EvmKeeper.Code(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.EqualValues(wantResp, gotResp, "want hex (%s), got hex (%s)", + collections.HumanizeBytes(wantResp.Code), + collections.HumanizeBytes(gotResp.Code), + ) + }) + } +} + +func (s *Suite) TestQueryParams() { + deps := evmtest.NewTestDeps() + want := evm.DefaultParams() + err := deps.EvmKeeper.SetParams(deps.Ctx, want) + s.NoError(err) + + gotResp, err := deps.EvmKeeper.Params(sdk.WrapSDKContext(deps.Ctx), nil) + s.NoError(err) + got := gotResp.Params + s.Require().NoError(err) + + // Note that protobuf equals is more reliable than `s.Equal` + s.Require().True(want.Equal(got), "want %s, got %s", want, got) + + // Empty params to test the setter + want.EVMChannels = []string{"channel-420"} + err = deps.EvmKeeper.SetParams(deps.Ctx, want) + s.NoError(err) + gotResp, err = deps.EvmKeeper.Params(sdk.WrapSDKContext(deps.Ctx), nil) + s.Require().NoError(err) + got = gotResp.Params + + // Note that protobuf equals is more reliable than `s.Equal` + s.Require().True(want.Equal(got), "want %s, got %s", want, got) +} + +func (s *Suite) TestQueryEthCall() { + type In = *evm.EthCallRequest + type Out = *evm.MsgEthereumTxResponse + testCases := []TestCase[In, Out]{ + { + name: "sad: msg invalid msg", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + return nil, nil + }, + wantErr: "InvalidArgument", + }, + { + name: "sad: invalid args", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + return &evm.EthCallRequest{Args: []byte("invalid")}, nil + }, + wantErr: "InvalidArgument", + }, + { + name: "happy: eth call to deploy erc20 contract", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + fungibleTokenContract := embeds.SmartContract_ERC20Minter + jsonTxArgs, err := json.Marshal(&evm.JsonTxArgs{ + From: &deps.Sender.EthAddr, + Data: (*hexutil.Bytes)(&fungibleTokenContract.Bytecode), + }) + s.Require().NoError(err) + return &evm.EthCallRequest{Args: jsonTxArgs}, &evm.MsgEthereumTxResponse{ + Hash: "0x0000000000000000000000000000000000000000000000000000000000000000", + } + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + gotResp, err := deps.App.EvmKeeper.EthCall(sdk.WrapSDKContext(deps.Ctx), req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.Assert().Empty(wantResp.VmError) + s.Assert().Equal(wantResp.Hash, gotResp.Hash) + }) + } +} + +func (s *Suite) TestQueryBalance() { + type In = *evm.QueryBalanceRequest + type Out = *evm.QueryBalanceResponse + testCases := []TestCase[In, Out]{ + { + name: "sad: msg validation", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryBalanceRequest{ + Address: InvalidEthAddr(), + } + wantResp = &evm.QueryBalanceResponse{ + BalanceWei: "0", + } + return req, wantResp + }, + wantErr: "InvalidArgument", + }, + { + name: "happy: zero balance", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryBalanceRequest{ + Address: evmtest.NewEthPrivAcc().EthAddr.String(), + } + wantResp = &evm.QueryBalanceResponse{ + Balance: "0", + BalanceWei: "0", + } + return req, wantResp + }, + wantErr: "", + }, + { + name: "happy: non zero balance", + setup: func(deps *evmtest.TestDeps) { + chain := deps.App + ethAddr := deps.Sender.EthAddr + + // fund account with 420 tokens + coins := sdk.Coins{sdk.NewInt64Coin(evm.EVMBankDenom, 420)} + err := chain.BankKeeper.MintCoins(deps.Ctx, evm.ModuleName, coins) + s.NoError(err) + err = chain.BankKeeper.SendCoinsFromModuleToAccount( + deps.Ctx, evm.ModuleName, ethAddr.Bytes(), coins) + s.Require().NoError(err) + }, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryBalanceRequest{ + Address: deps.Sender.EthAddr.Hex(), + } + wantResp = &evm.QueryBalanceResponse{ + Balance: "420", + BalanceWei: "420" + strings.Repeat("0", 12), + } + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.Balance(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.EqualValues(wantResp, gotResp) + }) + } +} + +func (s *Suite) TestQueryBaseFee() { + type In = *evm.QueryBaseFeeRequest + type Out = *evm.QueryBaseFeeResponse + testCases := []TestCase[In, Out]{ + { + name: "happy: base fee value", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryBaseFeeRequest{} + defaultFeeWei := math.NewIntFromBigInt(evm.BASE_FEE_WEI) + defaultFeeUnibi := math.NewIntFromBigInt(evm.BASE_FEE_MICRONIBI) + wantResp = &evm.QueryBaseFeeResponse{ + BaseFee: &defaultFeeWei, + BaseFeeUnibi: &defaultFeeUnibi, + } + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.BaseFee(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.EqualValues(wantResp, gotResp) + }) + } +} + +func (s *Suite) TestEstimateGasForEvmCallType() { + type In = *evm.EthCallRequest + type Out = *evm.EstimateGasResponse + testCases := []TestCase[In, Out]{ + { + name: "sad: nil query", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = nil + wantResp = nil + return req, wantResp + }, + wantErr: "InvalidArgument", + }, + { + name: "sad: insufficient gas cap", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.EthCallRequest{ + GasCap: gethparams.TxGas - 1, + } + return req, nil + }, + wantErr: "InvalidArgument", + }, + { + name: "sad: invalid args", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.EthCallRequest{ + Args: []byte{0, 0, 0}, + GasCap: gethparams.TxGas, + } + return req, nil + }, + wantErr: "InvalidArgument", + }, + { + name: "happy: estimate gas for transfer", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + // fund the account + chain := deps.App + ethAddr := deps.Sender.EthAddr + coins := sdk.Coins{sdk.NewInt64Coin(evm.EVMBankDenom, 1000)} + err := chain.BankKeeper.MintCoins(deps.Ctx, evm.ModuleName, coins) + s.NoError(err) + err = chain.BankKeeper.SendCoinsFromModuleToAccount( + deps.Ctx, evm.ModuleName, ethAddr.Bytes(), coins) + s.Require().NoError(err) + + // assert balance of 1000 * 10^12 wei + resp, _ := deps.App.EvmKeeper.Balance(sdk.WrapSDKContext(deps.Ctx), &evm.QueryBalanceRequest{ + Address: deps.Sender.EthAddr.Hex(), + }) + s.Equal("1000", resp.Balance) + s.Require().Equal("1000"+strings.Repeat("0", 12), resp.BalanceWei) + + // Send Eth call to transfer from the account - 5 * 10^12 wei + recipient := evmtest.NewEthPrivAcc().EthAddr + amountToSend := hexutil.Big(*evm.NativeToWei(big.NewInt(5))) + gasLimitArg := hexutil.Uint64(100000) + + jsonTxArgs, err := json.Marshal(&evm.JsonTxArgs{ + From: &deps.Sender.EthAddr, + To: &recipient, + Value: &amountToSend, + Gas: &gasLimitArg, + }) + s.Require().NoError(err) + req = &evm.EthCallRequest{ + Args: jsonTxArgs, + GasCap: gethparams.TxGas, + } + wantResp = &evm.EstimateGasResponse{ + Gas: gethparams.TxGas, + } + return req, wantResp + }, + wantErr: "", + onTestEnd: func(deps *evmtest.TestDeps) { + }, + }, + { + name: "sad: insufficient balance for transfer", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + recipient := evmtest.NewEthPrivAcc().EthAddr + amountToSend := hexutil.Big(*evm.NativeToWei(big.NewInt(10))) + + jsonTxArgs, err := json.Marshal(&evm.JsonTxArgs{ + From: &deps.Sender.EthAddr, + To: &recipient, + Value: &amountToSend, + }) + s.Require().NoError(err) + req = &evm.EthCallRequest{ + Args: jsonTxArgs, + GasCap: gethparams.TxGas, + } + wantResp = nil + return req, wantResp + }, + wantErr: "insufficient balance for transfer", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.EstimateGas(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.EqualValues(wantResp, gotResp) + + if tc.onTestEnd != nil { + tc.onTestEnd(&deps) + } + }) + } +} + +func (s *Suite) TestTraceTx() { + type In = *evm.QueryTraceTxRequest + type Out = string + + testCases := []TestCase[In, Out]{ + { + name: "sad: nil query", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + return nil, "" + }, + wantErr: "InvalidArgument", + }, + { + name: "happy: simple nibi transfer tx", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + txMsg, _ := evmtest.ExecuteNibiTransfer(deps, s.T()) + req = &evm.QueryTraceTxRequest{ + Msg: txMsg, + } + wantResp = TraceNibiTransfer() + return req, wantResp + }, + wantErr: "", + }, + { + name: "happy: trace erc-20 transfer tx", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + txMsg, predecessors, _ := evmtest.DeployAndExecuteERC20Transfer(deps, s.T()) + + req = &evm.QueryTraceTxRequest{ + Msg: txMsg, + Predecessors: predecessors, + } + wantResp = TraceERC20Transfer() + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, _ := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.TraceTx(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.Assert().NotNil(gotResp) + s.Assert().NotNil(gotResp.Data) + + // // Replace spaces in want resp + // re := regexp.MustCompile(`[\s\n\r]+`) + // wantResp = re.ReplaceAllString(wantResp, "") + // actualResp := string(gotResp.Data) + // if len(actualResp) > 1000 { + // actualResp = actualResp[:len(wantResp)] + // } + // // FIXME: Why does this trace sometimes have gas 35050 and sometimes 35062? + // // s.Equal(wantResp, actualResp) + // replaceTimes := 1 + // hackedWantResp := strings.Replace(wantResp, "35062", "35050", replaceTimes) + // s.True( + // wantResp == actualResp || hackedWantResp == actualResp, + // "got \"%s\", want \"%s\"", actualResp, wantResp, + // ) + }) + } +} + +func (s *Suite) TestTraceBlock() { + type In = *evm.QueryTraceBlockRequest + type Out = string + testCases := []TestCase[In, Out]{ + { + name: "sad: nil query", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + return nil, "" + }, + wantErr: "InvalidArgument", + }, + { + name: "happy: simple nibi transfer tx", + setup: nil, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + txMsg, _ := evmtest.ExecuteNibiTransfer(deps, s.T()) + req = &evm.QueryTraceBlockRequest{ + Txs: []*evm.MsgEthereumTx{ + txMsg, + }, + } + wantResp = "[{\"result\":" + TraceNibiTransfer() + "}]" + return req, wantResp + }, + wantErr: "", + }, + { + name: "happy: trace erc-20 transfer tx", + setup: nil, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + txMsg, _, _ := evmtest.DeployAndExecuteERC20Transfer(deps, s.T()) + req = &evm.QueryTraceBlockRequest{ + Txs: []*evm.MsgEthereumTx{ + txMsg, + }, + } + wantResp = "[{\"result\":" + TraceERC20Transfer() // no end as it's trimmed + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, _ := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.TraceBlock(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.Assert().NotNil(gotResp) + s.Assert().NotNil(gotResp.Data) + + // Replace spaces in want resp + // re := regexp.MustCompile(`[\s\n\r]+`) + // wantResp = re.ReplaceAllString(wantResp, "") + // actualResp := string(gotResp.Data) + // if len(actualResp) > 1000 { + // actualResp = actualResp[:len(wantResp)] + // } + // FIXME: Why does this trace sometimes have gas 35050 and sometimes 35062? + // s.Equal(wantResp, actualResp) + // replaceTimes := 1 + // hackedWantResp := strings.Replace(wantResp, "35062", "35050", replaceTimes) + // s.True( + // wantResp == actualResp || hackedWantResp == actualResp, + // "got \"%s\", want \"%s\"", actualResp, wantResp, + // ) + }) + } +} + +func (s *Suite) TestTraceCall() { + type In = *evm.QueryTraceTxRequest + type Out = string + + testCases := []TestCase[In, Out]{ + { + name: "sad: nil query", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + return nil, "" + }, + wantErr: "InvalidArgument", + }, + { + name: "happy: simple nibi transfer tx", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + recipient := evmtest.NewEthPrivAcc().EthAddr + gas := uint64(21000) + txArgs := evm.JsonTxArgs{ + From: &deps.Sender.EthAddr, + To: &recipient, + Value: (*hexutil.Big)(big.NewInt(1e12)), + Gas: (*hexutil.Uint64)(&gas), + } + req = &evm.QueryTraceTxRequest{ + Msg: txArgs.ToMsgEthTx(), + } + wantResp = TraceNibiTransfer() + return req, wantResp + }, + wantErr: "", + }, + { + name: "happy: trace erc-20 transfer tx", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + s.T().Log("Deploy ERC20") + + deployResp, err := evmtest.DeployContract( + deps, embeds.SmartContract_TestERC20, + ) + s.Require().NoError(err) + data, err := deployResp.ContractData.ABI.Pack( + "transfer", evmtest.NewEthPrivAcc().EthAddr, new(big.Int).SetUint64(1000), + ) + s.Require().NoError(err) + gas := uint64(1e6) + + txArgs := evm.JsonTxArgs{ + From: &deps.Sender.EthAddr, + To: &deployResp.ContractAddr, + Data: (*hexutil.Bytes)(&data), + Gas: (*hexutil.Uint64)(&gas), + } + req = &evm.QueryTraceTxRequest{ + Msg: txArgs.ToMsgEthTx(), + } + wantResp = TraceERC20Transfer() + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, _ := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.TraceCall(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.Assert().NotNil(gotResp) + s.Assert().NotNil(gotResp.Data) + }) + } +} + +func (s *Suite) TestQueryFunTokenMapping() { + type In = *evm.QueryFunTokenMappingRequest + type Out = *evm.QueryFunTokenMappingResponse + testCases := []TestCase[In, Out]{ + { + name: "sad: no token mapping", + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryFunTokenMappingRequest{ + Token: "unibi", + } + wantResp = &evm.QueryFunTokenMappingResponse{ + FunToken: nil, + } + return req, wantResp + }, + wantErr: "token mapping not found for unibi", + }, + { + name: "happy: token mapping exists from cosmos coin -> ERC20 token", + setup: func(deps *evmtest.TestDeps) { + _ = deps.EvmKeeper.FunTokens.SafeInsert( + deps.Ctx, + gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"), + "unibi", + true, + ) + }, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryFunTokenMappingRequest{ + Token: "unibi", + } + wantResp = &evm.QueryFunTokenMappingResponse{ + FunToken: &evm.FunToken{ + Erc20Addr: eth.EIP55Addr{ + Address: gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"), + }, + BankDenom: "unibi", + IsMadeFromCoin: true, + }, + } + return req, wantResp + }, + wantErr: "", + }, + { + name: "happy: token mapping exists from ERC20 token -> cosmos coin", + setup: func(deps *evmtest.TestDeps) { + _ = deps.EvmKeeper.FunTokens.SafeInsert( + deps.Ctx, + gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"), + "unibi", + true, + ) + }, + scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) { + req = &evm.QueryFunTokenMappingRequest{ + Token: "0xAEf9437FF23D48D73271a41a8A094DEc9ac71477", + } + wantResp = &evm.QueryFunTokenMappingResponse{ + FunToken: &evm.FunToken{ + Erc20Addr: eth.EIP55Addr{ + Address: gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"), + }, + BankDenom: "unibi", + IsMadeFromCoin: true, + }, + } + return req, wantResp + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + if tc.setup != nil { + tc.setup(&deps) + } + req, wantResp := tc.scenario(&deps) + goCtx := sdk.WrapSDKContext(deps.Ctx) + gotResp, err := deps.EvmKeeper.FunTokenMapping(goCtx, req) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Assert().NoError(err) + s.EqualValues(wantResp, gotResp) + }) + } +} diff --git a/x/evm/keeper/hooks.go b/x/evm/keeper/hooks.go new file mode 100644 index 000000000..f42c5ae1e --- /dev/null +++ b/x/evm/keeper/hooks.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/NibiruChain/nibiru/v2/eth" + + "github.com/NibiruChain/nibiru/v2/x/evm" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethcoretypes "github.com/ethereum/go-ethereum/core/types" +) + +// BeginBlock hook for the EVM module. +func (k *Keeper) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock also retrieves the bloom filter value from the transient store and commits it to the +func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + bloom := gethcoretypes.BytesToBloom(k.EvmState.GetBlockBloomTransient(ctx).Bytes()) + _ = ctx.EventManager().EmitTypedEvent(&evm.EventBlockBloom{ + Bloom: eth.BloomToHex(bloom), + }) + // The bloom logic doesn't update the validator set. + return []abci.ValidatorUpdate{} +} diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go new file mode 100644 index 000000000..19de7266d --- /dev/null +++ b/x/evm/keeper/keeper.go @@ -0,0 +1,140 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/vm" + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/cometbft/cometbft/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/x/common/omap" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +type Keeper struct { + cdc codec.BinaryCodec + // storeKey: For persistent storage of EVM state. + storeKey storetypes.StoreKey + // transientKey: Store key that resets every block during Commit + transientKey storetypes.StoreKey + + // EvmState isolates the key-value stores (collections) for the x/evm module. + EvmState EvmState + + // FunTokens isolates the key-value stores (collections) for fungible token + // mappings. + FunTokens FunTokenState + + // the address capable of executing a MsgUpdateParams message. Typically, + // this should be the x/gov module account. + authority sdk.AccAddress + + Bank *NibiruBankKeeper + accountKeeper evm.AccountKeeper + stakingKeeper evm.StakingKeeper + + // precompiles is the set of active precompiled contracts used in the EVM. + // Precompiles are special, built-in contract interfaces that exist at + // predefined address and run custom logic outside of what is possible only + // in Solidity. + precompiles omap.SortedMap[gethcommon.Address, vm.PrecompiledContract] + + // tracer: Configures the output type for a geth `vm.EVMLogger`. Tracer types + // include "access_list", "json", "struct", and "markdown". If any other + // value is used, a no operation tracer is set. + tracer string +} + +// NewKeeper is a constructor for an x/evm [Keeper]. This function is necessary +// because the [Keeper] struct has private fields. +func NewKeeper( + cdc codec.BinaryCodec, + storeKey, transientKey storetypes.StoreKey, + authority sdk.AccAddress, + accKeeper evm.AccountKeeper, + bankKeeper *NibiruBankKeeper, + stakingKeeper evm.StakingKeeper, + tracer string, +) Keeper { + if err := sdk.VerifyAddressFormat(authority); err != nil { + panic(err) + } + + return Keeper{ + cdc: cdc, + storeKey: storeKey, + transientKey: transientKey, + authority: authority, + EvmState: NewEvmState(cdc, storeKey, transientKey), + FunTokens: NewFunTokenState(cdc, storeKey), + accountKeeper: accKeeper, + Bank: bankKeeper, + stakingKeeper: stakingKeeper, + tracer: tracer, + } +} + +// GetEvmGasBalance: Used in the EVM Ante Handler, +// "github.com/NibiruChain/nibiru/v2/app/evmante": Load account's balance of gas +// tokens for EVM execution in EVM denom units. +func (k *Keeper) GetEvmGasBalance(ctx sdk.Context, addr gethcommon.Address) (balance *big.Int) { + nibiruAddr := sdk.AccAddress(addr.Bytes()) + return k.Bank.GetBalance(ctx, nibiruAddr, evm.EVMBankDenom).Amount.BigInt() +} + +func (k Keeper) EthChainID(ctx sdk.Context) *big.Int { + return appconst.GetEthChainID(ctx.ChainID()) +} + +// BaseFeeMicronibiPerGas returns the gas base fee in units of the EVM denom. Note +// that this function is currently constant/stateless. +func (k Keeper) BaseFeeMicronibiPerGas(_ sdk.Context) *big.Int { + // TODO: (someday maybe): Consider making base fee dynamic based on + // congestion in the previous block. + return evm.BASE_FEE_MICRONIBI +} + +// BaseFeeWeiPerGas is the same as BaseFeeMicronibiPerGas, except its in units of +// wei per gas. +func (k Keeper) BaseFeeWeiPerGas(_ sdk.Context) *big.Int { + return evm.NativeToWei(k.BaseFeeMicronibiPerGas(sdk.Context{})) +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", evm.ModuleName) +} + +// Tracer return a default vm.Tracer based on current keeper state +func (k Keeper) Tracer( + ctx sdk.Context, msg core.Message, ethCfg *gethparams.ChainConfig, +) vm.EVMLogger { + return evm.NewTracer(k.tracer, msg, ethCfg, ctx.BlockHeight()) +} + +// HandleOutOfGasPanic gracefully captures "out of gas" panic and just sets the value to err +func HandleOutOfGasPanic(err *error, format string) func() { + return func() { + if r := recover(); r != nil { + switch r.(type) { + case sdk.ErrorOutOfGas: + *err = vm.ErrOutOfGas + default: + panic(r) + } + } + if err != nil && *err != nil && format != "" { + *err = fmt.Errorf("%s: %w", format, *err) + } + } +} diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go new file mode 100644 index 000000000..584ff21bf --- /dev/null +++ b/x/evm/keeper/keeper_test.go @@ -0,0 +1,16 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type Suite struct { + suite.Suite +} + +// TestSuite: Runs all the tests in the suite. +func TestSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} diff --git a/x/evm/keeper/msg_ethereum_tx_test.go b/x/evm/keeper/msg_ethereum_tx_test.go new file mode 100644 index 000000000..c09122f89 --- /dev/null +++ b/x/evm/keeper/msg_ethereum_tx_test.go @@ -0,0 +1,274 @@ +package keeper_test + +import ( + "math/big" + "strconv" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + gethcore "github.com/ethereum/go-ethereum/core/types" + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/x/evm" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *Suite) TestMsgEthereumTx_CreateContract() { + testCases := []struct { + name string + scenario func() + }{ + { + name: "happy: deploy contract, sufficient gas limit", + scenario: func() { + deps := evmtest.NewTestDeps() + ethAcc := deps.Sender + + // Leftover gas fee is refunded within EthereumTx from the FeeCollector + // so, the module must have some coins + err := testapp.FundModuleAccount( + deps.App.BankKeeper, + deps.Ctx, + authtypes.FeeCollectorName, + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(1_000_000))), + ) + s.Require().NoError(err) + s.T().Log("create eth tx msg, increase gas limit") + gasLimit := big.NewInt(1_500_000) + args := evmtest.ArgsCreateContract{ + EthAcc: ethAcc, + EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), + GasPrice: big.NewInt(1), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), + GasLimit: gasLimit, + } + ethTxMsg, err := evmtest.CreateContractMsgEthereumTx(args) + s.Require().NoError(err) + s.Require().NoError(ethTxMsg.ValidateBasic()) + s.Equal(ethTxMsg.GetGas(), gasLimit.Uint64()) + + resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) + s.Require().NoError( + err, + "resp: %s\nblock header: %s", + resp, + deps.Ctx.BlockHeader().ProposerAddress, + ) + s.Require().Empty(resp.VmError) + + // Event "EventContractDeployed" must present + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventContractDeployed{ + Sender: ethAcc.EthAddr.String(), + ContractAddr: resp.Logs[0].Address, + }, + ) + }, + }, + { + name: "sad: deploy contract, exceed gas limit", + scenario: func() { + deps := evmtest.NewTestDeps() + ethAcc := deps.Sender + + s.T().Log("create eth tx msg, default create contract gas") + gasLimit := gethparams.TxGasContractCreation + args := evmtest.ArgsCreateContract{ + EthAcc: ethAcc, + EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), + GasPrice: big.NewInt(1), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), + } + ethTxMsg, err := evmtest.CreateContractMsgEthereumTx(args) + s.NoError(err) + s.Require().NoError(ethTxMsg.ValidateBasic()) + s.Equal(ethTxMsg.GetGas(), gasLimit) + + resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) + s.Require().ErrorContains( + err, + core.ErrIntrinsicGas.Error(), + "resp: %s\nblock header: %s", + resp, + deps.Ctx.BlockHeader().ProposerAddress, + ) + }, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, tc.scenario) + } +} + +func (s *Suite) TestMsgEthereumTx_ExecuteContract() { + deps := evmtest.NewTestDeps() + ethAcc := deps.Sender + + // Leftover gas fee is refunded within EthereumTx from the FeeCollector + // so, the module must have some coins + err := testapp.FundModuleAccount( + deps.App.BankKeeper, + deps.Ctx, + authtypes.FeeCollectorName, + sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(1000_000))), + ) + s.Require().NoError(err) + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestERC20, + ) + s.Require().NoError(err) + contractAddr := deployResp.ContractAddr + testContract := embeds.SmartContract_TestERC20 + to := gethcommon.HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed") + input, err := testContract.ABI.Pack("transfer", to, big.NewInt(123)) + s.NoError(err) + + gasLimit := big.NewInt(1000_000) + args := evmtest.ArgsExecuteContract{ + EthAcc: ethAcc, + EthChainIDInt: deps.EvmKeeper.EthChainID(deps.Ctx), + GasPrice: big.NewInt(1), + Nonce: deps.NewStateDB().GetNonce(ethAcc.EthAddr), + GasLimit: gasLimit, + ContractAddress: &contractAddr, + Data: input, + } + ethTxMsg, err := evmtest.ExecuteContractMsgEthereumTx(args) + s.NoError(err) + s.Require().NoError(ethTxMsg.ValidateBasic()) + s.Equal(ethTxMsg.GetGas(), gasLimit.Uint64()) + resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) + s.Require().NoError( + err, + "resp: %s\nblock header: %s", + resp, + deps.Ctx.BlockHeader().ProposerAddress, + ) + s.Require().Empty(resp.VmError) + + // Event "EventContractExecuted" must present + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventContractExecuted{ + Sender: ethAcc.EthAddr.String(), + ContractAddr: resp.Logs[0].Address, + }, + ) +} + +func (s *Suite) TestMsgEthereumTx_SimpleTransfer() { + testCases := []struct { + name string + txType evmtest.GethTxType + }{ + { + name: "happy: AccessListTx", + txType: gethcore.AccessListTxType, + }, + { + name: "happy: LegacyTx", + txType: gethcore.LegacyTxType, + }, + } + + for _, tc := range testCases { + deps := evmtest.NewTestDeps() + ethAcc := deps.Sender + + fundedAmount := evm.NativeToWei(big.NewInt(123)).Int64() + err := testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewInt64Coin("unibi", fundedAmount)), + ) + s.Require().NoError(err) + + s.T().Log("create eth tx msg") + var innerTxData []byte = nil + var accessList gethcore.AccessList = nil + to := gethcommon.HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed") + + ethTxMsg, err := evmtest.NewEthTxMsgFromTxData( + &deps, + tc.txType, + innerTxData, + deps.NewStateDB().GetNonce(ethAcc.EthAddr), + &to, + big.NewInt(fundedAmount), + gethparams.TxGas, + accessList, + ) + s.NoError(err) + + resp, err := deps.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(deps.Ctx), ethTxMsg) + s.Require().NoError(err) + s.Require().Empty(resp.VmError) + + gasUsed := strconv.FormatUint(resp.GasUsed, 10) + wantGasUsed := strconv.FormatUint(gethparams.TxGas, 10) + s.Equal(gasUsed, wantGasUsed) + + // Event "EventTransfer" must present + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventTransfer{ + Sender: ethAcc.EthAddr.String(), + Recipient: to.String(), + Amount: strconv.FormatInt(fundedAmount, 10), + }, + ) + } +} + +func (s *Suite) TestEthereumTx_ABCI() { + deps := evmtest.NewTestDeps() + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420))), + )) + + blockHeader := deps.Ctx.BlockHeader() + // blockHeader := tmproto.Header{Height: deps.Ctx.BlockHeight()} + deps.App.BeginBlock(abci.RequestBeginBlock{Header: blockHeader}) + to := evmtest.NewEthPrivAcc() + evmTxMsg, err := evmtest.TxTransferWei{ + Deps: &deps, + To: to.EthAddr, + AmountWei: evm.NativeToWei(big.NewInt(420)), + }.Build() + s.NoError(err) + + txBuilder := deps.EncCfg.TxConfig.NewTxBuilder() + blockTx, err := evmTxMsg.BuildTx(txBuilder, evm.EVMBankDenom) + s.Require().NoError(err) + + txBz, err := deps.EncCfg.TxConfig.TxEncoder()(blockTx) + s.Require().NoError(err) + deliverTxResp := deps.App.DeliverTx(abci.RequestDeliverTx{Tx: txBz}) + s.Require().True(deliverTxResp.IsOK(), "%#v", deliverTxResp) + deps.App.EndBlock(abci.RequestEndBlock{Height: deps.Ctx.BlockHeight()}) + + { + r := deliverTxResp + s.EqualValuesf(21000, r.GasUsed, "%d", r.GasUsed) + s.EqualValuesf(21000, r.GasWanted, "%d", r.GasWanted) + } +} diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go new file mode 100644 index 000000000..ac69d3b36 --- /dev/null +++ b/x/evm/keeper/msg_server.go @@ -0,0 +1,740 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "context" + "encoding/binary" + "fmt" + "math/big" + "strconv" + + "cosmossdk.io/errors" + tmbytes "github.com/cometbft/cometbft/libs/bytes" + tmtypes "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +var _ evm.MsgServer = &Keeper{} + +func (k *Keeper) EthereumTx( + goCtx context.Context, txMsg *evm.MsgEthereumTx, +) (evmResp *evm.MsgEthereumTxResponse, err error) { + // This is a `defer` pattern to add behavior that runs in the case that the + // error is non-nil, creating a concise way to add extra information. + defer func() { + if err != nil { + err = fmt.Errorf("EthereumTx error: %w", err) + } + }() + + if err := txMsg.ValidateBasic(); err != nil { + return evmResp, errors.Wrap(err, "EthereumTx validate basic failed") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + tx := txMsg.AsTransaction() + txConfig := k.TxConfig(ctx, tx.Hash()) + evmCfg := k.GetEVMConfig(ctx) + + // get the signer according to the chain rules from the config and block height + evmMsg, err := tx.AsMessage(gethcore.NewLondonSigner(evmCfg.ChainConfig.ChainID), evmCfg.BaseFeeWei) + if err != nil { + return nil, errors.Wrap(err, "failed to convert ethereum transaction as core message") + } + + // ApplyEvmMsg - Perform the EVM State transition + stateDB := k.Bank.StateDB + if stateDB == nil { + stateDB = k.NewStateDB(ctx, txConfig) + } + defer func() { + k.Bank.StateDB = nil + }() + evmObj := k.NewEVM(ctx, evmMsg, evmCfg, nil /*tracer*/, stateDB) + evmResp, err = k.ApplyEvmMsg( + ctx, + evmMsg, + evmObj, + nil, /*tracer*/ + true, /*commit*/ + txConfig.TxHash, + ) + if evmResp != nil { + ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "execute ethereum tx") + } + if err != nil { + return nil, errors.Wrap(err, "error applying ethereum core message") + } + + k.updateBlockBloom(ctx, evmResp, uint64(txConfig.LogIndex)) + + // refund gas in order to match the Ethereum gas consumption instead of the + // default SDK one. + refundGas := uint64(0) + if evmMsg.Gas() > evmResp.GasUsed { + refundGas = evmMsg.Gas() - evmResp.GasUsed + } + weiPerGas := txMsg.EffectiveGasPriceWeiPerGas(evmCfg.BaseFeeWei) + if err = k.RefundGas(ctx, evmMsg.From(), refundGas, weiPerGas); err != nil { + return nil, errors.Wrapf(err, "error refunding leftover gas to sender %s", evmMsg.From()) + } + + err = k.EmitEthereumTxEvents(ctx, tx.To(), tx.Type(), evmMsg, evmResp) + if err != nil { + return nil, errors.Wrap(err, "error emitting ethereum tx events") + } + + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err != nil { + return nil, errors.Wrap(err, "error emitting tx log event") + } + + k.EvmState.BlockTxIndex.Set(ctx, uint64(txConfig.TxIndex)+1) + + return evmResp, nil +} + +// NewEVM generates a go-ethereum VM. +// +// Args: +// - ctx: Consensus and KV store info for the current block. +// - msg: Ethereum message sent to a contract +// - cfg: Encapsulates params required to construct an EVM. +// - tracer: Collects execution traces for EVM transaction logging. +// - stateDB: Holds the EVM state. +func (k *Keeper) NewEVM( + ctx sdk.Context, + msg core.Message, + evmCfg statedb.EVMConfig, + tracer vm.EVMLogger, + stateDB vm.StateDB, +) *vm.EVM { + pseudoRandomBytes := make([]byte, 8) + binary.BigEndian.PutUint64(pseudoRandomBytes, uint64(ctx.BlockHeader().Time.UnixNano())) + pseudoRandom := crypto.Keccak256Hash(append(pseudoRandomBytes, ctx.BlockHeader().LastCommitHash...)) + + blockCtx := vm.BlockContext{ + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetHash: k.GetHashFn(ctx), + Coinbase: evmCfg.BlockCoinbase, + GasLimit: eth.BlockGasLimit(ctx), + BlockNumber: big.NewInt(ctx.BlockHeight()), + Time: big.NewInt(ctx.BlockHeader().Time.Unix()), + Difficulty: big.NewInt(0), // unused. Only required in PoW context + BaseFee: evmCfg.BaseFeeWei, + Random: &pseudoRandom, + } + + txCtx := core.NewEVMTxContext(msg) + if tracer == nil { + tracer = k.Tracer(ctx, msg, evmCfg.ChainConfig) + } + vmConfig := k.VMConfig(ctx, &evmCfg, tracer) + theEvm := vm.NewEVM(blockCtx, txCtx, stateDB, evmCfg.ChainConfig, vmConfig) + theEvm.WithPrecompiles(k.precompiles.InternalData(), k.precompiles.Keys()) + return theEvm +} + +// GetHashFn implements vm.GetHashFunc for Ethermint. It handles 3 cases: +// 1. The requested height matches the current height from context (and thus same epoch number) +// 2. The requested height is from a previous height from the same chain epoch +// 3. The requested height is from a height greater than the latest one +func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { + return func(height uint64) gethcommon.Hash { + h, err := eth.SafeInt64(height) + if err != nil { + k.Logger(ctx).Error("failed to cast height to int64", "error", err) + return gethcommon.Hash{} + } + + switch { + case ctx.BlockHeight() == h: + // Case 1: The requested height matches the one from the context, so + // we can retrieve the header hash directly from the context. Note: + // The headerHash is only set at begin block, it will be nil in case + // of a query context + headerHash := ctx.HeaderHash() + if len(headerHash) != 0 { + return gethcommon.BytesToHash(headerHash) + } + + // only recompute the hash if not set (eg: checkTxState) + contextBlockHeader := ctx.BlockHeader() + header, err := tmtypes.HeaderFromProto(&contextBlockHeader) + if err != nil { + k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err) + return gethcommon.Hash{} + } + + headerHash = header.Hash() + return gethcommon.BytesToHash(headerHash) + + case ctx.BlockHeight() > h: + // Case 2: if the chain is not the current height we need to retrieve + // the hash from the store for the current chain epoch. This only + // applies if the current height is greater than the requested + // height. + histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, h) + if !found { + k.Logger(ctx).Debug("historical info not found", "height", h) + return gethcommon.Hash{} + } + + header, err := tmtypes.HeaderFromProto(&histInfo.Header) + if err != nil { + k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err) + return gethcommon.Hash{} + } + + return gethcommon.BytesToHash(header.Hash()) + default: + // Case 3: heights greater than the current one returns an empty hash. + return gethcommon.Hash{} + } + } +} + +// ApplyEvmMsg computes the new state by applying the given message against the +// existing state. If the message fails, the VM execution error with the reason +// will be returned to the client and the transaction won't be committed to the +// store. +// +// ## Reverted state +// +// The snapshot and rollback are supported by the `statedb.StateDB`. +// +// ## Different Callers +// +// It's called in three scenarios: +// 1. `ApplyTransaction`, in the transaction processing flow. +// 2. `EthCall/EthEstimateGas` grpc query handler. +// 3. Called by other native modules directly. +// +// ## Prechecks and Preprocessing +// +// All relevant state transition prechecks for the MsgEthereumTx are performed on the AnteHandler, +// prior to running the transaction against the state. The prechecks run are the following: +// +// 1. the nonce of the message caller is correct +// 2. caller has enough balance to cover transaction fee(gaslimit * gasprice) +// 3. the amount of gas required is available in the block +// 4. the purchased gas is enough to cover intrinsic usage +// 5. there is no overflow when calculating intrinsic gas +// 6. caller has enough balance to cover asset transfer for **topmost** call +// +// The preprocessing steps performed by the AnteHandler are: +// +// 1. set up the initial access list +// +// ## Tracer parameter +// +// It should be a `vm.Tracer` object or nil, if pass `nil`, it'll create a +// default one based on keeper options. +// +// ## Commit parameter +// +// If commit is true, the `StateDB` will be committed, otherwise discarded. +// +// ## fullRefundLeftoverGas parameter +// +// For internal calls like funtokens, user does not specify gas limit explicitly. +// In this case we don't apply any caps for refund and refund 100% +func (k *Keeper) ApplyEvmMsg( + ctx sdk.Context, + msg core.Message, + evmObj *vm.EVM, + tracer vm.EVMLogger, + commit bool, + txHash gethcommon.Hash, +) (resp *evm.MsgEthereumTxResponse, err error) { + gasRemaining := msg.Gas() + + // Allow the tracer captures the tx level events, mainly the gas consumption. + vmCfg := evmObj.Config + if vmCfg.Debug { + vmCfg.Tracer.CaptureTxStart(gasRemaining) + defer func() { + vmCfg.Tracer.CaptureTxEnd(gasRemaining) + }() + } + + contractCreation := msg.To() == nil + + intrinsicGasCost, err := core.IntrinsicGas( + msg.Data(), msg.AccessList(), + contractCreation, true, true, + ) + if err != nil { + // should have already been checked on Ante Handler + return nil, errors.Wrap(err, "ApplyEvmMsg: intrinsic gas overflowed") + } + + // Check if the provided gas in the message is enough to cover the intrinsic + // gas, the base gas cost before execution occurs (gethparams.TxGas, contract + // creation, and cost per byte of the data payload). + // + // Should check again even if it is checked on Ante Handler, because eth_call + // don't go through Ante Handler. + if gasRemaining < intrinsicGasCost { + // eth_estimateGas will check for this exact error + return nil, errors.Wrapf( + core.ErrIntrinsicGas, + "ApplyEvmMsg: provided msg.Gas (%d) is less than intrinsic gas cost (%d)", + gasRemaining, intrinsicGasCost, + ) + } + gasRemaining -= intrinsicGasCost + + // access list preparation is moved from ante handler to here, because it's + // needed when `ApplyMessage` is called under contexts where ante handlers + // are not run, for example `eth_call` and `eth_estimateGas`. + evmObj.StateDB.PrepareAccessList( + msg.From(), + msg.To(), + evmObj.ActivePrecompiles(params.Rules{}), + msg.AccessList(), + ) + + msgWei, err := ParseWeiAsMultipleOfMicronibi(msg.Value()) + if err != nil { + return nil, errors.Wrapf(err, "ApplyEvmMsg: invalid wei amount %s", msg.Value()) + } + + // take over the nonce management from evm: + // - reset sender's nonce to msg.Nonce() before calling evm. + // - increase sender's nonce by one no matter the result. + evmObj.StateDB.SetNonce(msg.From(), msg.Nonce()) + + var returnBz []byte + var vmErr error + if contractCreation { + returnBz, _, gasRemaining, vmErr = evmObj.Create( + vm.AccountRef(msg.From()), + msg.Data(), + gasRemaining, + msgWei, + ) + } else { + returnBz, gasRemaining, vmErr = evmObj.Call( + vm.AccountRef(msg.From()), + *msg.To(), + msg.Data(), + gasRemaining, + msgWei, + ) + } + // Increment nonce after processing the message + evmObj.StateDB.SetNonce(msg.From(), msg.Nonce()+1) + + // EVM execution error needs to be available for the JSON-RPC client + var vmError string + if vmErr != nil { + vmError = vmErr.Error() + } + + // process gas refunds (we refund a portion of the unused gas) + gasUsed := msg.Gas() - gasRemaining + // please see https://eips.ethereum.org/EIPS/eip-3529 for why we do refunds + refundAmount := gasToRefund(evmObj.StateDB.GetRefund(), gasUsed) + gasRemaining += refundAmount + gasUsed -= refundAmount + + evmResp := &evm.MsgEthereumTxResponse{ + GasUsed: gasUsed, + VmError: vmError, + Ret: returnBz, + Logs: evm.NewLogsFromEth(evmObj.StateDB.(*statedb.StateDB).Logs()), + Hash: txHash.Hex(), + } + + if gasRemaining > msg.Gas() { // rare case of overflow + evmResp.GasUsed = msg.Gas() // cap the gas used to the original gas limit + return evmResp, errors.Wrapf(core.ErrGasUintOverflow, "ApplyEvmMsg: message gas limit (%d) < leftover gas (%d)", msg.Gas(), gasRemaining) + } + + // The dirty states in `StateDB` is either committed or discarded after return + if commit { + if err := evmObj.StateDB.(*statedb.StateDB).Commit(); err != nil { + return evmResp, errors.Wrap(err, "ApplyEvmMsg: failed to commit stateDB") + } + } + + return evmResp, nil +} + +func ParseWeiAsMultipleOfMicronibi(weiInt *big.Int) (newWeiInt *big.Int, err error) { + // if "weiValue" is nil, 0, or negative, early return + if weiInt == nil || !(weiInt.Cmp(big.NewInt(0)) > 0) { + return weiInt, nil + } + + // err if weiInt is too small + tenPow12 := new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil) + if weiInt.Cmp(tenPow12) < 0 { + return weiInt, fmt.Errorf( + "wei amount is too small (%s), cannot transfer less than 1 micronibi. 1 NIBI == 10^6 micronibi == 10^18 wei", weiInt) + } + + // truncate to highest micronibi amount + newWeiInt = evm.NativeToWei(evm.WeiToNative(weiInt)) + return newWeiInt, nil +} + +// CreateFunToken is a gRPC transaction message for creating fungible token +// ("FunToken") a mapping between a bank coin and ERC20 token. +// +// If the mapping is generated from an ERC20, this tx creates a bank coin to go +// with it, and if the mapping's generated from a coin, the EVM module +// deploys an ERC20 contract that for which it will be the owner. +func (k *Keeper) CreateFunToken( + goCtx context.Context, msg *evm.MsgCreateFunToken, +) (resp *evm.MsgCreateFunTokenResponse, err error) { + var funtoken *evm.FunToken + err = msg.ValidateBasic() + if err != nil { + return nil, err + } + + // Deduct fee upon registration. + ctx := sdk.UnwrapSDKContext(goCtx) + err = k.deductCreateFunTokenFee(ctx, msg) + if err != nil { + return nil, err + } + + emptyErc20 := msg.FromErc20 == nil || msg.FromErc20.Size() == 0 + switch { + case !emptyErc20 && msg.FromBankDenom == "": + funtoken, err = k.createFunTokenFromERC20(ctx, msg.FromErc20.Address) + case emptyErc20 && msg.FromBankDenom != "": + funtoken, err = k.createFunTokenFromCoin(ctx, msg.FromBankDenom) + default: + // Impossible to reach this case due to ValidateBasic + err = fmt.Errorf( + "either the \"from_erc20\" or \"from_bank_denom\" must be set (but not both)") + } + if err != nil { + return nil, err + } + + _ = ctx.EventManager().EmitTypedEvent(&evm.EventFunTokenCreated{ + Creator: msg.Sender, + BankDenom: funtoken.BankDenom, + Erc20ContractAddress: funtoken.Erc20Addr.String(), + IsMadeFromCoin: emptyErc20, + }) + + return &evm.MsgCreateFunTokenResponse{ + FuntokenMapping: *funtoken, + }, err +} + +func (k Keeper) deductCreateFunTokenFee(ctx sdk.Context, msg *evm.MsgCreateFunToken) error { + fee := k.FeeForCreateFunToken(ctx) + from := sdk.MustAccAddressFromBech32(msg.Sender) // validation in msg.ValidateBasic + + if err := k.Bank.SendCoinsFromAccountToModule( + ctx, from, evm.ModuleName, fee); err != nil { + return fmt.Errorf("unable to pay the \"create_fun_token_fee\": %w", err) + } + if err := k.Bank.BurnCoins(ctx, evm.ModuleName, fee); err != nil { + return fmt.Errorf("failed to burn the \"create_fun_token_fee\" after payment: %w", err) + } + return nil +} + +func (k Keeper) FeeForCreateFunToken(ctx sdk.Context) sdk.Coins { + evmParams := k.GetParams(ctx) + return sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, evmParams.CreateFuntokenFee)) +} + +// ConvertCoinToEvm Sends a coin with a valid "FunToken" mapping to the +// given recipient address ("to_eth_addr") in the corresponding ERC20 +// representation. +func (k *Keeper) ConvertCoinToEvm( + goCtx context.Context, msg *evm.MsgConvertCoinToEvm, +) (resp *evm.MsgConvertCoinToEvmResponse, err error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + sender := sdk.MustAccAddressFromBech32(msg.Sender) + + funTokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, msg.BankCoin.Denom)) + if len(funTokens) == 0 { + return nil, fmt.Errorf("funtoken for bank denom \"%s\" does not exist", msg.BankCoin.Denom) + } + if len(funTokens) > 1 { + return nil, fmt.Errorf("multiple funtokens for bank denom \"%s\" found", msg.BankCoin.Denom) + } + + fungibleTokenMapping := funTokens[0] + + if fungibleTokenMapping.IsMadeFromCoin { + return k.convertCoinToEvmBornCoin( + ctx, sender, msg.ToEthAddr.Address, msg.BankCoin, fungibleTokenMapping, + ) + } else { + return k.convertCoinToEvmBornERC20( + ctx, sender, msg.ToEthAddr.Address, msg.BankCoin, fungibleTokenMapping, + ) + } +} + +// Converts Bank Coins for FunToken mapping that was born from a coin +// (IsMadeFromCoin=true) into the ERC20 tokens. EVM module owns the ERC-20 +// contract and can mint the ERC-20 tokens. +func (k Keeper) convertCoinToEvmBornCoin( + ctx sdk.Context, + sender sdk.AccAddress, + recipient gethcommon.Address, + coin sdk.Coin, + funTokenMapping evm.FunToken, +) (*evm.MsgConvertCoinToEvmResponse, error) { + // 1 | Send Bank Coins to the EVM module + err := k.Bank.SendCoinsFromAccountToModule(ctx, sender, evm.ModuleName, sdk.NewCoins(coin)) + if err != nil { + return nil, errors.Wrap(err, "failed to send coins to module account") + } + + // 2 | Mint ERC20 tokens to the recipient + erc20Addr := funTokenMapping.Erc20Addr.Address + contractInput, err := embeds.SmartContract_ERC20Minter.ABI.Pack("mint", recipient, coin.Amount.BigInt()) + if err != nil { + return nil, err + } + evmMsg := gethcore.NewMessage( + evm.EVM_MODULE_ADDRESS, + &erc20Addr, + k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS), + big.NewInt(0), + Erc20GasLimitExecute, + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + contractInput, + gethcore.AccessList{}, + true, + ) + txConfig := k.TxConfig(ctx, gethcommon.Hash{}) + stateDB := k.Bank.StateDB + if stateDB == nil { + stateDB = k.NewStateDB(ctx, txConfig) + } + defer func() { + k.Bank.StateDB = nil + }() + + evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) + evmResp, err := k.CallContractWithInput( + ctx, + evmObj, + evm.EVM_MODULE_ADDRESS, + &erc20Addr, + true, /*commit*/ + contractInput, + Erc20GasLimitExecute, + ) + if err != nil { + return nil, err + } + + if evmResp.Failed() { + return nil, + fmt.Errorf("failed to mint erc-20 tokens of contract %s", erc20Addr.String()) + } + + if err = stateDB.Commit(); err != nil { + return nil, errors.Wrap(err, "failed to commit stateDB") + } + + _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertCoinToEvm{ + Sender: sender.String(), + Erc20ContractAddress: erc20Addr.String(), + ToEthAddr: recipient.String(), + BankCoin: coin, + }) + + return &evm.MsgConvertCoinToEvmResponse{}, nil +} + +// Converts a coin that was originally an ERC20 token, and that was converted to +// a bank coin, back to an ERC20 token. EVM module does not own the ERC-20 +// contract and cannot mint the ERC-20 tokens. EVM module has escrowed tokens in +// the first conversion from ERC-20 to bank coin. +func (k Keeper) convertCoinToEvmBornERC20( + ctx sdk.Context, + sender sdk.AccAddress, + recipient gethcommon.Address, + coin sdk.Coin, + funTokenMapping evm.FunToken, +) (*evm.MsgConvertCoinToEvmResponse, error) { + // needs to run first to populate the StateDB on the BankKeeperExtension + stateDB := k.Bank.StateDB + if stateDB == nil { + stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{})) + } + defer func() { + k.Bank.StateDB = nil + }() + + erc20Addr := funTokenMapping.Erc20Addr.Address + // 1 | Caller transfers Bank Coins to be converted to ERC20 tokens. + if err := k.Bank.SendCoinsFromAccountToModule( + ctx, + sender, + evm.ModuleName, + sdk.NewCoins(coin), + ); err != nil { + return nil, errors.Wrap(err, "error sending Bank Coins to the EVM") + } + + // 3 | In the FunToken ERC20 → BC conversion process that preceded this + // TxMsg, the Bank Coins were minted. Consequently, to preserve an invariant + // on the sum of the FunToken's bank and ERC20 supply, we burn the coins here + // in the BC → ERC20 conversion. + if err := k.Bank.BurnCoins(ctx, evm.ModuleName, sdk.NewCoins(coin)); err != nil { + return nil, errors.Wrap(err, "failed to burn coins") + } + + // 2 | EVM sends ERC20 tokens to the "to" account. + // This should never fail due to the EVM account lacking ERc20 fund because + // the account must have sent the EVM module ERC20 tokens in the mapping + // in order to create the coins originally. + // + // Said another way, if an asset is created as an ERC20 and some amount is + // converted to its Bank Coin representation, a balance of the ERC20 is left + // inside the EVM module account in order to convert the coins back to + // ERC20s. + contractInput, err := embeds.SmartContract_ERC20Minter.ABI.Pack("transfer", recipient, coin.Amount.BigInt()) + if err != nil { + return nil, err + } + evmMsg := gethcore.NewMessage( + evm.EVM_MODULE_ADDRESS, + &erc20Addr, + k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS), + big.NewInt(0), + Erc20GasLimitExecute, + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + contractInput, + gethcore.AccessList{}, + true, + ) + evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) + _, _, err = k.ERC20().Transfer( + erc20Addr, + evm.EVM_MODULE_ADDRESS, + recipient, + coin.Amount.BigInt(), + ctx, + evmObj, + ) + if err != nil { + return nil, errors.Wrap(err, "failed to transfer ERC-20 tokens") + } + + // Commit the stateDB to the BankKeeperExtension because we don't go through + // ApplyEvmMsg at all in this tx. + if err := stateDB.Commit(); err != nil { + return nil, errors.Wrap(err, "failed to commit stateDB") + } + + // Emit event with the actual amount received + _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertCoinToEvm{ + Sender: sender.String(), + Erc20ContractAddress: funTokenMapping.Erc20Addr.String(), + ToEthAddr: recipient.String(), + BankCoin: coin, + }) + + return &evm.MsgConvertCoinToEvmResponse{}, nil +} + +// EmitEthereumTxEvents emits all types of EVM events applicable to a particular execution case +func (k *Keeper) EmitEthereumTxEvents( + ctx sdk.Context, + recipient *gethcommon.Address, + txType uint8, + msg gethcore.Message, + evmResp *evm.MsgEthereumTxResponse, +) error { + // Typed event: eth.evm.v1.EventEthereumTx + eventEthereumTx := &evm.EventEthereumTx{ + EthHash: evmResp.Hash, + Index: strconv.FormatUint(k.EvmState.BlockTxIndex.GetOr(ctx, 0), 10), + GasUsed: strconv.FormatUint(evmResp.GasUsed, 10), + } + if len(ctx.TxBytes()) > 0 { + eventEthereumTx.Hash = tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()).String() + } + if recipient != nil { + eventEthereumTx.Recipient = recipient.Hex() + } + if evmResp.Failed() { + eventEthereumTx.VmError = evmResp.VmError + } + err := ctx.EventManager().EmitTypedEvent(eventEthereumTx) + if err != nil { + return errors.Wrap(err, "EmitEthereumTxEvents: failed to emit event ethereum tx") + } + + // Untyped event: "message", used for tendermint subscription + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, evm.ModuleName), + sdk.NewAttribute(sdk.AttributeKeySender, msg.From().Hex()), + sdk.NewAttribute(evm.MessageEventAttrTxType, fmt.Sprintf("%d", txType)), + ), + ) + + // Emit typed events + if !evmResp.Failed() { + if recipient == nil { // contract creation + contractAddr := crypto.CreateAddress(msg.From(), msg.Nonce()) + _ = ctx.EventManager().EmitTypedEvent(&evm.EventContractDeployed{ + Sender: msg.From().Hex(), + ContractAddr: contractAddr.String(), + }) + } else if len(msg.Data()) > 0 { // contract executed + _ = ctx.EventManager().EmitTypedEvent(&evm.EventContractExecuted{ + Sender: msg.From().Hex(), + ContractAddr: msg.To().String(), + }) + } else if msg.Value().Cmp(big.NewInt(0)) > 0 { // evm transfer + _ = ctx.EventManager().EmitTypedEvent(&evm.EventTransfer{ + Sender: msg.From().Hex(), + Recipient: msg.To().Hex(), + Amount: msg.Value().String(), + }) + } + } + + return nil +} + +// updateBlockBloom updates transient block bloom filter +func (k *Keeper) updateBlockBloom( + ctx sdk.Context, + evmResp *evm.MsgEthereumTxResponse, + logIndex uint64, +) { + if len(evmResp.Logs) > 0 { + logs := evm.LogsToEthereum(evmResp.Logs) + k.EvmState.BlockBloom.Set(ctx, k.EvmState.CalcBloomFromLogs(ctx, logs).Bytes()) + k.EvmState.BlockLogSize.Set(ctx, logIndex+uint64(len(logs))) + } +} diff --git a/x/evm/keeper/msg_update_params.go b/x/evm/keeper/msg_update_params.go new file mode 100644 index 000000000..12dd71fe8 --- /dev/null +++ b/x/evm/keeper/msg_update_params.go @@ -0,0 +1,26 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "context" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +func (k *Keeper) UpdateParams( + goCtx context.Context, req *evm.MsgUpdateParams, +) (resp *evm.MsgUpdateParamsResponse, err error) { + if k.authority.String() != req.Authority { + return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority, expected %s, got %s", k.authority.String(), req.Authority) + } + ctx := sdk.UnwrapSDKContext(goCtx) + err = k.SetParams(ctx, req.Params) + if err != nil { + return nil, errors.Wrapf(err, "failed to set params") + } + return &evm.MsgUpdateParamsResponse{}, nil +} diff --git a/x/evm/keeper/precompiles.go b/x/evm/keeper/precompiles.go new file mode 100644 index 000000000..3b1a0e480 --- /dev/null +++ b/x/evm/keeper/precompiles.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/x/common/omap" +) + +func (k *Keeper) AddPrecompiles( + precompileMap map[gethcommon.Address]vm.PrecompiledContract, +) { + if k.precompiles.Len() == 0 { + k.precompiles = omap.SortedMap_EthAddress( + precompileMap, + ) + } else { + for addr, precompile := range precompileMap { + k.precompiles.Set(addr, precompile) + } + } +} + +func (k *Keeper) IsPrecompile(addr gethcommon.Address) bool { + return k.precompiles.Has(addr) +} diff --git a/x/evm/keeper/random_test.go b/x/evm/keeper/random_test.go new file mode 100644 index 000000000..b08ee2065 --- /dev/null +++ b/x/evm/keeper/random_test.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "time" + + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +// TestRandom tests the random value generation within the EVM. +func (s *Suite) TestRandom() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + deployResp, err := evmtest.DeployContract(&deps, embeds.SmartContract_TestRandom) + s.Require().NoError(err) + randomContractAddr := deployResp.ContractAddr + + // highjacked LoadERC20BigInt method as it perfectly fits the need of this test + random1, err := deps.EvmKeeper.ERC20().LoadERC20BigInt( + deps.Ctx, evmObj, embeds.SmartContract_TestRandom.ABI, randomContractAddr, "getRandom", + ) + s.Require().NoError(err) + s.Require().NotNil(random1) + s.Require().NotZero(random1.Int64()) + + // Update block time to check that random changes + deps.Ctx = deps.Ctx.WithBlockTime(deps.Ctx.BlockTime().Add(1 * time.Second)) + evmObj, _ = deps.NewEVM() + random2, err := deps.EvmKeeper.ERC20().LoadERC20BigInt( + deps.Ctx, evmObj, embeds.SmartContract_TestRandom.ABI, randomContractAddr, "getRandom", + ) + s.Require().NoError(err) + s.Require().NotNil(random1) + s.Require().NotZero(random2.Int64()) + s.Require().NotEqual(random1, random2) +} diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go new file mode 100644 index 000000000..f46c491e6 --- /dev/null +++ b/x/evm/keeper/statedb.go @@ -0,0 +1,205 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "math/big" + + sdkmath "cosmossdk.io/math" + "github.com/NibiruChain/collections" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +var _ statedb.Keeper = &Keeper{} + +// ---------------------------------------------------------------------------- +// StateDB Keeper implementation +// ---------------------------------------------------------------------------- + +// GetAccount: Ethereum account getter for a [statedb.Account]. +// Implements the `statedb.Keeper` interface. +// Returns nil if the account does not exist or has the wrong type. +func (k *Keeper) GetAccount(ctx sdk.Context, addr gethcommon.Address) *statedb.Account { + acct := k.getAccountWithoutBalance(ctx, addr) + if acct == nil { + return nil + } + + acct.BalanceNative = k.GetEvmGasBalance(ctx, addr) + return acct +} + +// GetCode: Loads smart contract bytecode. +// Implements the `statedb.Keeper` interface. +func (k *Keeper) GetCode(ctx sdk.Context, codeHash gethcommon.Hash) []byte { + codeBz, err := k.EvmState.ContractBytecode.Get(ctx, codeHash.Bytes()) + if err != nil { + panic(err) // TODO: We don't like to panic. + } + return codeBz +} + +// ForEachStorage: Iterator over contract storage. +// Implements the `statedb.Keeper` interface. +func (k *Keeper) ForEachStorage( + ctx sdk.Context, + addr gethcommon.Address, + stopIter func(key, value gethcommon.Hash) bool, +) { + iter := k.EvmState.AccState.Iterate( + ctx, + collections.PairRange[gethcommon.Address, gethcommon.Hash]{}.Prefix(addr), + ) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + hash := iter.Key().K2() + val := iter.Value() + if !stopIter(hash, gethcommon.BytesToHash(val)) { + return + } + } +} + +// SetAccBalance update account's balance, compare with current balance first, +// then decide to mint or burn. +// Implements the `statedb.Keeper` interface. +// Only called by `StateDB.Commit()`. +func (k *Keeper) SetAccBalance( + ctx sdk.Context, addr gethcommon.Address, amountEvmDenom *big.Int, +) error { + addrBech32 := eth.EthAddrToNibiruAddr(addr) + balance := k.Bank.GetBalance(ctx, addrBech32, evm.EVMBankDenom).Amount.BigInt() + delta := new(big.Int).Sub(amountEvmDenom, balance) + bk := k.Bank.BaseKeeper + + switch delta.Sign() { + case 1: + // mint + coins := sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdkmath.NewIntFromBigInt(delta))) + if err := bk.MintCoins(ctx, evm.ModuleName, coins); err != nil { + return err + } + if err := bk.SendCoinsFromModuleToAccount(ctx, evm.ModuleName, addrBech32, coins); err != nil { + return err + } + case -1: + // burn + coins := sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdkmath.NewIntFromBigInt(new(big.Int).Neg(delta)))) + if err := bk.SendCoinsFromAccountToModule(ctx, addrBech32, evm.ModuleName, coins); err != nil { + return err + } + if err := bk.BurnCoins(ctx, evm.ModuleName, coins); err != nil { + return err + } + default: + // not changed + } + return nil +} + +// SetAccount: Updates nonce, balance, and codeHash. +// Implements the `statedb.Keeper` interface. +// Only called by `StateDB.Commit()`. +func (k *Keeper) SetAccount( + ctx sdk.Context, addr gethcommon.Address, account statedb.Account, +) error { + // update account + nibiruAddr := sdk.AccAddress(addr.Bytes()) + acct := k.accountKeeper.GetAccount(ctx, nibiruAddr) + if acct == nil { + acct = k.accountKeeper.NewAccountWithAddress(ctx, nibiruAddr) + } + + if err := acct.SetSequence(account.Nonce); err != nil { + return err + } + + codeHash := gethcommon.BytesToHash(account.CodeHash) + + if ethAcct, ok := acct.(eth.EthAccountI); ok { + if err := ethAcct.SetCodeHash(codeHash); err != nil { + return err + } + } + + k.accountKeeper.SetAccount(ctx, acct) + + if err := k.SetAccBalance(ctx, addr, account.BalanceNative); err != nil { + return err + } + + return nil +} + +// SetState: Update contract storage, delete if value is empty. +// Implements the `statedb.Keeper` interface. +// Only called by `StateDB.Commit()`. +func (k *Keeper) SetState( + ctx sdk.Context, addr gethcommon.Address, stateKey gethcommon.Hash, stateValue []byte, +) { + k.EvmState.SetAccState(ctx, addr, stateKey, stateValue) +} + +// SetCode: Setter for smart contract bytecode. Delete if code is empty. +// Implements the `statedb.Keeper` interface. +// Only called by `StateDB.Commit()`. +func (k *Keeper) SetCode(ctx sdk.Context, codeHash, code []byte) { + k.EvmState.SetAccCode(ctx, codeHash, code) +} + +// DeleteAccount handles contract's suicide call, clearing the balance, contract +// bytecode, contract state, and its native account. +// Implements the `statedb.Keeper` interface. +// Only called by `StateDB.Commit()`. +func (k *Keeper) DeleteAccount(ctx sdk.Context, addr gethcommon.Address) error { + nibiruAddr := sdk.AccAddress(addr.Bytes()) + acct := k.accountKeeper.GetAccount(ctx, nibiruAddr) + if acct == nil { + return nil + } + + _, ok := acct.(eth.EthAccountI) + if !ok { + return evm.ErrInvalidAccount.Wrapf("type %T, address %s", acct, addr) + } + + // clear balance + if err := k.SetAccBalance(ctx, addr, new(big.Int)); err != nil { + return err + } + + // clear storage + k.ForEachStorage(ctx, addr, func(key, _ gethcommon.Hash) bool { + k.SetState(ctx, addr, key, nil) + return true + }) + + k.accountKeeper.RemoveAccount(ctx, acct) + + return nil +} + +// getAccountWithoutBalance load nonce and codehash without balance, +// more efficient in cases where balance is not needed. +func (k *Keeper) getAccountWithoutBalance(ctx sdk.Context, addr gethcommon.Address) *statedb.Account { + acct := k.accountKeeper.GetAccount(ctx, eth.EthAddrToNibiruAddr(addr)) + if acct == nil { + return nil + } + + codeHash := evm.EmptyCodeHash + ethAcct, ok := acct.(eth.EthAccountI) + if ok { + codeHash = ethAcct.GetCodeHash().Bytes() + } + + return &statedb.Account{ + Nonce: acct.GetSequence(), + CodeHash: codeHash, + } +} diff --git a/x/evm/keeper/statedb_test.go b/x/evm/keeper/statedb_test.go new file mode 100644 index 000000000..3ed34c838 --- /dev/null +++ b/x/evm/keeper/statedb_test.go @@ -0,0 +1,101 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "math/big" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + + gethcommon "github.com/ethereum/go-ethereum/common" +) + +// TestStateDBBalance tests the behavior of the StateDB with regards to account +// balances, ensuring correct conversion between native tokens (unibi) and EVM +// tokens (wei), as well as proper balance updates during transfers. +func (s *Suite) TestStateDBBalance() { + deps := evmtest.NewTestDeps() + { + db := deps.NewStateDB() + s.Equal("0", db.GetBalance(deps.Sender.EthAddr).String()) + + s.T().Log("fund account in unibi. See expected wei amount.") + err := testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 42)), + ) + s.NoError(err) + s.Equal( + "42"+strings.Repeat("0", 12), + db.GetBalance(deps.Sender.EthAddr).String(), + ) + s.Equal( + "42", + deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom).Amount.String(), + ) + } + + s.T().Log("Send via EVM transfer. See expected wei amounts.") + to := gethcommon.HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed") + { + _, err := evmtest.TxTransferWei{ + Deps: &deps, + To: to, + AmountWei: evm.NativeToWei(big.NewInt(12)), + }.Run() + s.Require().NoError(err) + db := deps.NewStateDB() + s.Equal( + "30"+strings.Repeat("0", 12), + db.GetBalance(deps.Sender.EthAddr).String(), + ) + s.Equal( + "12"+strings.Repeat("0", 12), + db.GetBalance(to).String(), + ) + + s.T().Log("Send via EVM transfer with too little wei. Should error") + _, err = evmtest.TxTransferWei{ + Deps: &deps, + To: to, + AmountWei: big.NewInt(12), + }.Run() + s.Require().ErrorContains(err, "wei amount is too small") + } + + s.T().Log("Send via bank transfer from account to account. See expected wei amounts.") + { + deps := evmtest.NewTestDeps() + toNibiAddr := eth.EthAddrToNibiruAddr(to) + err := testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 8)), + ) + s.NoError(err) + err = deps.App.BankKeeper.SendCoins( + deps.Ctx, deps.Sender.NibiruAddr, + toNibiAddr, + sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 3)), + ) + s.NoError(err) + + db := deps.NewStateDB() + s.Equal( + "3"+strings.Repeat("0", 12), + db.GetBalance(to).String(), + ) + s.Equal( + "5"+strings.Repeat("0", 12), + db.GetBalance(deps.Sender.EthAddr).String(), + ) + } +} diff --git a/x/evm/keeper/vm_config.go b/x/evm/keeper/vm_config.go new file mode 100644 index 000000000..1bf53fbbd --- /dev/null +++ b/x/evm/keeper/vm_config.go @@ -0,0 +1,80 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +func (k *Keeper) GetEVMConfig(ctx sdk.Context) statedb.EVMConfig { + return statedb.EVMConfig{ + Params: k.GetParams(ctx), + ChainConfig: evm.EthereumConfig(appconst.GetEthChainID(ctx.ChainID())), + BlockCoinbase: k.GetCoinbaseAddress(ctx), + BaseFeeWei: k.BaseFeeWeiPerGas(ctx), + } +} + +// TxConfig loads `TxConfig` from current transient storage +func (k *Keeper) TxConfig( + ctx sdk.Context, txHash common.Hash, +) statedb.TxConfig { + return statedb.TxConfig{ + BlockHash: common.BytesToHash(ctx.HeaderHash()), + TxHash: txHash, + TxIndex: uint(k.EvmState.BlockTxIndex.GetOr(ctx, 0)), + LogIndex: uint(k.EvmState.BlockLogSize.GetOr(ctx, 0)), + } +} + +// VMConfig creates an EVM configuration from the debug setting and the extra +// EIPs enabled on the module parameters. The config generated uses the default +// JumpTable from the EVM. +func (k Keeper) VMConfig( + ctx sdk.Context, cfg *statedb.EVMConfig, tracer vm.EVMLogger, +) vm.Config { + var debug bool + if _, ok := tracer.(evm.NoOpTracer); !ok { + debug = true + } + + return vm.Config{ + Debug: debug, + Tracer: tracer, + NoBaseFee: false, + ExtraEips: cfg.Params.EIPs(), + } +} + +// GetCoinbaseAddress returns the block proposer's validator operator address. +// In Ethereum, the coinbase (or "benficiary") is the address that proposed the +// current block. It corresponds to the [COINBASE op code]. +// +// [COINBASE op code]: https://ethereum.org/en/developers/docs/evm/opcodes/ +func (k Keeper) GetCoinbaseAddress(ctx sdk.Context) common.Address { + proposerAddress := sdk.ConsAddress(ctx.BlockHeader().ProposerAddress) + validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, proposerAddress) + if !found { + // should never happen, but just in case, return an empty address + // we don't really care about the coinbase adresss since we're PoS and not PoW + return common.Address{} + } + + return common.BytesToAddress(validator.GetOperator()) +} + +// ParseProposerAddr returns current block proposer's address when provided +// proposer address is empty. +func ParseProposerAddr( + ctx sdk.Context, proposerAddress sdk.ConsAddress, +) sdk.ConsAddress { + if len(proposerAddress) == 0 { + proposerAddress = ctx.BlockHeader().ProposerAddress + } + return proposerAddress +} diff --git a/x/evm/logs.go b/x/evm/logs.go new file mode 100644 index 000000000..d83742216 --- /dev/null +++ b/x/evm/logs.go @@ -0,0 +1,87 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "errors" + "fmt" + + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// Validate performs a basic validation of an ethereum Log fields. +func (log *Log) Validate() error { + if err := eth.ValidateAddress(log.Address); err != nil { + return fmt.Errorf("invalid log address %w", err) + } + if eth.IsEmptyHash(log.BlockHash) { + return fmt.Errorf("block hash cannot be the empty %s", log.BlockHash) + } + if log.BlockNumber == 0 { + return errors.New("block number cannot be zero") + } + if eth.IsEmptyHash(log.TxHash) { + return fmt.Errorf("tx hash cannot be the empty %s", log.TxHash) + } + return nil +} + +// ToEthereum returns the Ethereum type Log from a Ethermint proto compatible Log. +func (log *Log) ToEthereum() *gethcore.Log { + topics := make([]gethcommon.Hash, len(log.Topics)) + for i, topic := range log.Topics { + topics[i] = gethcommon.HexToHash(topic) + } + + return &gethcore.Log{ + Address: gethcommon.HexToAddress(log.Address), + Topics: topics, + Data: log.Data, + BlockNumber: log.BlockNumber, + TxHash: gethcommon.HexToHash(log.TxHash), + TxIndex: uint(log.TxIndex), + Index: uint(log.Index), + BlockHash: gethcommon.HexToHash(log.BlockHash), + Removed: log.Removed, + } +} + +func NewLogsFromEth(ethlogs []*gethcore.Log) []Log { + var logs []Log //nolint: prealloc + for _, ethlog := range ethlogs { + logs = append(logs, NewLogFromEth(ethlog)) + } + + return logs +} + +// LogsToEthereum casts the Proto Logs to a slice of Ethereum Logs. +func LogsToEthereum(logs []Log) []*gethcore.Log { + var ethLogs []*gethcore.Log //nolint: prealloc + for i := range logs { + ethLogs = append(ethLogs, logs[i].ToEthereum()) + } + return ethLogs +} + +// NewLogFromEth creates a new Log instance from an Ethereum type Log. +func NewLogFromEth(log *gethcore.Log) Log { + topics := make([]string, len(log.Topics)) + for i, topic := range log.Topics { + topics[i] = topic.String() + } + + return Log{ + Address: log.Address.String(), + Topics: topics, + Data: log.Data, + BlockNumber: log.BlockNumber, + TxHash: log.TxHash.String(), + TxIndex: uint64(log.TxIndex), + Index: uint64(log.Index), + BlockHash: log.BlockHash.String(), + Removed: log.Removed, + } +} diff --git a/x/evm/logs_test.go b/x/evm/logs_test.go new file mode 100644 index 000000000..97c3e67f1 --- /dev/null +++ b/x/evm/logs_test.go @@ -0,0 +1,85 @@ +package evm_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + + "github.com/ethereum/go-ethereum/common" +) + +func TestValidateLog(t *testing.T) { + addr := evmtest.NewEthPrivAcc().EthAddr.String() + + testCases := []struct { + name string + log *evm.Log + expPass bool + }{ + { + "valid log", + &evm.Log{ + Address: addr, + Topics: []string{common.BytesToHash([]byte("topic")).String()}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: common.BytesToHash([]byte("tx_hash")).String(), + TxIndex: 1, + BlockHash: common.BytesToHash([]byte("block_hash")).String(), + Index: 1, + Removed: false, + }, + true, + }, + { + "empty log", &evm.Log{}, false, + }, + { + "zero address", + &evm.Log{ + Address: common.Address{}.String(), + }, + false, + }, + { + "empty block hash", + &evm.Log{ + Address: addr, + BlockHash: common.Hash{}.String(), + }, + false, + }, + { + "zero block number", + &evm.Log{ + Address: addr, + BlockHash: common.BytesToHash([]byte("block_hash")).String(), + BlockNumber: 0, + }, + false, + }, + { + "empty tx hash", + &evm.Log{ + Address: addr, + BlockHash: common.BytesToHash([]byte("block_hash")).String(), + BlockNumber: 1, + TxHash: common.Hash{}.String(), + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.log.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/evm/msg.go b/x/evm/msg.go new file mode 100644 index 000000000..c0839bd80 --- /dev/null +++ b/x/evm/msg.go @@ -0,0 +1,519 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "errors" + "fmt" + "math/big" + + "github.com/cosmos/gogoproto/proto" + + sdkmath "cosmossdk.io/math" + + errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + + "github.com/NibiruChain/nibiru/v2/eth" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" +) + +var ( + _ sdk.Msg = &MsgEthereumTx{} + _ sdk.Tx = &MsgEthereumTx{} + _ ante.GasTx = &MsgEthereumTx{} + _ sdk.Msg = &MsgUpdateParams{} + _ sdk.Msg = &MsgCreateFunToken{} + + _ codectypes.UnpackInterfacesMessage = MsgEthereumTx{} +) + +// NewTx returns a reference to a new Ethereum transaction message. +func NewTx( + tx *EvmTxArgs, +) *MsgEthereumTx { + var ( + cid, amt, gp *sdkmath.Int + toAddr string + txData TxData + ) + + if tx.To != nil { + toAddr = tx.To.Hex() + } + + if tx.Amount != nil { + amountInt := sdkmath.NewIntFromBigInt(tx.Amount) + amt = &amountInt + } + + if tx.ChainID != nil { + chainIDInt := sdkmath.NewIntFromBigInt(tx.ChainID) + cid = &chainIDInt + } + + if tx.GasPrice != nil { + gasPriceInt := sdkmath.NewIntFromBigInt(tx.GasPrice) + gp = &gasPriceInt + } + + switch { + case tx.GasFeeCap != nil: + gtc := sdkmath.NewIntFromBigInt(tx.GasTipCap) + gfc := sdkmath.NewIntFromBigInt(tx.GasFeeCap) + + txData = &DynamicFeeTx{ + ChainID: cid, + Amount: amt, + To: toAddr, + GasTipCap: >c, + GasFeeCap: &gfc, + Nonce: tx.Nonce, + GasLimit: tx.GasLimit, + Data: tx.Input, + Accesses: NewAccessList(tx.Accesses), + } + case tx.Accesses != nil: + txData = &AccessListTx{ + ChainID: cid, + Nonce: tx.Nonce, + To: toAddr, + Amount: amt, + GasLimit: tx.GasLimit, + GasPrice: gp, + Data: tx.Input, + Accesses: NewAccessList(tx.Accesses), + } + default: + txData = &LegacyTx{ + To: toAddr, + Amount: amt, + GasPrice: gp, + Nonce: tx.Nonce, + GasLimit: tx.GasLimit, + Data: tx.Input, + } + } + + dataAny, err := PackTxData(txData) + if err != nil { + panic(err) + } + + msg := MsgEthereumTx{Data: dataAny} + msg.Hash = msg.AsTransaction().Hash().Hex() + return &msg +} + +// FromEthereumTx populates the message fields from the given ethereum transaction +func (msg *MsgEthereumTx) FromEthereumTx(tx *gethcore.Transaction) error { + txData, err := NewTxDataFromTx(tx) + if err != nil { + return err + } + + anyTxData, err := PackTxData(txData) + if err != nil { + return err + } + + msg.Data = anyTxData + msg.Hash = tx.Hash().Hex() + return nil +} + +// Route returns the route value of an MsgEthereumTx. +func (msg MsgEthereumTx) Route() string { return RouterKey } + +func (msg MsgEthereumTx) Type() string { return proto.MessageName(new(MsgEthereumTx)) } + +// ValidateBasic implements the sdk.Msg interface. It performs basic validation +// checks of a Transaction. If returns an error if validation fails. +func (msg MsgEthereumTx) ValidateBasic() error { + if msg.From != "" { + if err := eth.ValidateAddress(msg.From); err != nil { + return errorsmod.Wrap(err, "invalid from address") + } + } + + // Validate Size_ field, should be kept empty + if msg.Size_ != 0 { + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "tx size is deprecated") + } + + txData, err := UnpackTxData(msg.Data) + if err != nil { + return errorsmod.Wrap(err, "failed to unpack tx data") + } + + gas := txData.GetGas() + + // prevent txs with 0 gas to fill up the mempool + if gas == 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "gas limit must not be zero") + } + + // prevent gas limit from overflow + if g := new(big.Int).SetUint64(gas); !g.IsInt64() { + return errorsmod.Wrap(core.ErrGasUintOverflow, "gas limit must be less than math.MaxInt64") + } + + if err := txData.Validate(); err != nil { + return errorsmod.Wrap(err, "failed \"TxData.Validate\"") + } + + // Validate EthHash field after validated txData to avoid panic + txHash := msg.AsTransaction().Hash().Hex() + if msg.Hash != txHash { + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid tx hash %s, expected: %s", msg.Hash, txHash) + } + + return nil +} + +// GetMsgs returns a single MsgEthereumTx as sdk.Msg. +func (msg *MsgEthereumTx) GetMsgs() []sdk.Msg { + return []sdk.Msg{msg} +} + +// GetSigners returns the expected signers for an Ethereum transaction message. +// For such a message, there should exist only a single 'signer'. +// +// NOTE: This method panics if 'Sign' hasn't been called first. +func (msg *MsgEthereumTx) GetSigners() []sdk.AccAddress { + data, err := UnpackTxData(msg.Data) + if err != nil { + panic(err) + } + + sender, err := msg.GetSender(data.GetChainID()) + if err != nil { + panic(err) + } + + signer := sdk.AccAddress(sender.Bytes()) + return []sdk.AccAddress{signer} +} + +// GetSignBytes returns the Amino bytes of an Ethereum transaction message used +// for signing. +// +// NOTE: This method cannot be used as a chain ID is needed to create valid bytes +// to sign over. Use 'RLPSignBytes' instead. +func (msg MsgEthereumTx) GetSignBytes() []byte { + panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign") +} + +// Sign calculates a secp256k1 ECDSA signature and signs the transaction. It +// takes a keyring signer and the chainID to sign an Ethereum transaction according to +// EIP155 standard. +// This method mutates the transaction as it populates the V, R, S +// fields of the Transaction's Signature. +// The function will fail if the sender address is not defined for the msg or if +// the sender is not registered on the keyring +func (msg *MsgEthereumTx) Sign(ethSigner gethcore.Signer, keyringSigner keyring.Signer) error { + from := msg.GetFrom() + if from.Empty() { + return fmt.Errorf("sender address not defined for message") + } + + tx := msg.AsTransaction() + txHash := ethSigner.Hash(tx) + + sig, _, err := keyringSigner.SignByAddress(from, txHash.Bytes()) + if err != nil { + return err + } + + tx, err = tx.WithSignature(ethSigner, sig) + if err != nil { + return err + } + + return msg.FromEthereumTx(tx) +} + +// GetGas implements the GasTx interface. It returns the GasLimit of the transaction. +func (msg MsgEthereumTx) GetGas() uint64 { + txData, err := UnpackTxData(msg.Data) + if err != nil { + return 0 + } + return txData.GetGas() +} + +// GetFee returns the fee for non dynamic fee tx +func (msg MsgEthereumTx) GetFee() *big.Int { + txData, err := UnpackTxData(msg.Data) + if err != nil { + return nil + } + return txData.Fee() +} + +// EffectiveFeeWei returns the fee for dynamic fee tx +func (msg MsgEthereumTx) EffectiveFeeWei(baseFee *big.Int) *big.Int { + txData, err := UnpackTxData(msg.Data) + if err != nil { + return nil + } + return txData.EffectiveFeeWei(baseFee) +} + +// EffectiveGasPriceWeiPerGas returns the effective gas price according to the base +// fee. This value is in units of "wei per unit gas". +func (msg MsgEthereumTx) EffectiveGasPriceWeiPerGas(baseFeeWei *big.Int) *big.Int { + txData, err := UnpackTxData(msg.Data) + if err != nil { + return nil + } + return txData.EffectiveGasPriceWeiPerGas(baseFeeWei) +} + +func (msg MsgEthereumTx) EffectiveGasCapWei(baseFeeWei *big.Int) *big.Int { + txData, err := UnpackTxData(msg.Data) + if err != nil { + return nil + } + return txData.EffectiveGasFeeCapWei(baseFeeWei) +} + +// GetFrom loads the ethereum sender address from the sigcache and returns an +// sdk.AccAddress from its bytes +func (msg *MsgEthereumTx) GetFrom() sdk.AccAddress { + if msg.From == "" { + return nil + } + + return common.HexToAddress(msg.From).Bytes() +} + +// AsTransaction creates an Ethereum Transaction type from the msg fields +func (msg MsgEthereumTx) AsTransaction() *gethcore.Transaction { + txData, err := UnpackTxData(msg.Data) + if err != nil { + return nil + } + + return gethcore.NewTx(txData.AsEthereumData()) +} + +// AsMessage creates an Ethereum core.Message from the msg fields +func (msg MsgEthereumTx) AsMessage(signer gethcore.Signer, baseFeeWei *big.Int) (core.Message, error) { + return msg.AsTransaction().AsMessage(signer, baseFeeWei) +} + +// GetSender extracts the sender address from the signature values using the latest signer for the given chainID. +func (msg *MsgEthereumTx) GetSender(chainID *big.Int) (common.Address, error) { + signer := gethcore.LatestSignerForChainID(chainID) + from, err := signer.Sender(msg.AsTransaction()) + if err != nil { + return common.Address{}, err + } + + msg.From = from.Hex() + return from, nil +} + +// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces +func (msg MsgEthereumTx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + return unpacker.UnpackAny(msg.Data, new(TxData)) +} + +// UnmarshalBinary decodes the canonical encoding of transactions. +func (msg *MsgEthereumTx) UnmarshalBinary(b []byte) error { + tx := &gethcore.Transaction{} + if err := tx.UnmarshalBinary(b); err != nil { + return err + } + return msg.FromEthereumTx(tx) +} + +// BuildTx builds the Cosmos-SDK [signing.Tx] from ethereum tx ([MsgEthereumTx]) +func (msg *MsgEthereumTx) BuildTx(b client.TxBuilder, evmDenom string) (signing.Tx, error) { + builder, ok := b.(authtx.ExtensionOptionsTxBuilder) + if !ok { + return nil, errors.New("unsupported builder") + } + + option, err := codectypes.NewAnyWithValue(&ExtensionOptionsEthereumTx{}) + if err != nil { + return nil, err + } + + txData, err := UnpackTxData(msg.Data) + if err != nil { + return nil, err + } + + // Compute fees using effective fee to enforce 1unibi minimum gas price + fees := make(sdk.Coins, 0) + effectiveFeeMicronibi := WeiToNative(txData.EffectiveFeeWei(BASE_FEE_WEI)) + feeAmtMicronibi := sdkmath.NewIntFromBigInt(effectiveFeeMicronibi) + if feeAmtMicronibi.Sign() > 0 { + fees = append(fees, sdk.NewCoin(evmDenom, feeAmtMicronibi)) + } + + builder.SetExtensionOptions(option) + + // A valid msg should have empty `From` + msg.From = "" + + err = builder.SetMsgs(msg) + if err != nil { + return nil, err + } + builder.SetFeeAmount(fees) + builder.SetGasLimit(msg.GetGas()) + tx := builder.GetTx() + return tx, nil +} + +// GetSigners returns the expected signers for a MsgUpdateParams message. +func (m MsgUpdateParams) GetSigners() []sdk.AccAddress { + //#nosec G703 -- gosec raises a warning about a non-handled error which we deliberately ignore here + addr, _ := sdk.AccAddressFromBech32(m.Authority) + return []sdk.AccAddress{addr} +} + +// ValidateBasic does a sanity check of the provided data +func (m *MsgUpdateParams) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { + return errorsmod.Wrap(err, "invalid authority address") + } + + return m.Params.Validate() +} + +// GetSignBytes implements the LegacyMsg interface. +func (m MsgUpdateParams) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&m)) +} + +// UnwrapEthereumMsg extracts MsgEthereumTx from wrapping sdk.Tx +func UnwrapEthereumMsg(tx *sdk.Tx, ethHash common.Hash) (*MsgEthereumTx, error) { + if tx == nil { + return nil, fmt.Errorf("invalid tx: nil") + } + + for _, msg := range (*tx).GetMsgs() { + ethMsg, ok := msg.(*MsgEthereumTx) + if !ok { + return nil, fmt.Errorf("invalid tx type: %T", tx) + } + txHash := ethMsg.AsTransaction().Hash() + ethMsg.Hash = txHash.Hex() + if txHash == ethHash { + return ethMsg, nil + } + } + + return nil, fmt.Errorf("eth tx not found: %s", ethHash) +} + +// DecodeTxResponse decodes an protobuf-encoded byte slice into TxResponse +func DecodeTxResponse(in []byte) (*MsgEthereumTxResponse, error) { + var txMsgData sdk.TxMsgData + if err := proto.Unmarshal(in, &txMsgData); err != nil { + return nil, err + } + + if len(txMsgData.MsgResponses) == 0 { + return &MsgEthereumTxResponse{}, nil + } + + var res MsgEthereumTxResponse + if err := proto.Unmarshal(txMsgData.MsgResponses[0].Value, &res); err != nil { + return nil, errorsmod.Wrap(err, "failed to unmarshal tx response message data") + } + + return &res, nil +} + +var EmptyCodeHash = crypto.Keccak256(nil) + +// BinSearch executes the binary search and hone in on an executable gas limit +func BinSearch( + lo, hi uint64, executable func(uint64) (bool, *MsgEthereumTxResponse, error), +) (uint64, error) { + for lo+1 < hi { + mid := (hi + lo) / 2 + failed, _, err := executable(mid) + // If this errors, there was a consensus error, and the provided message + // call or tx will never be accepted, regardless of how high we set the + // gas limit. + // Return the error directly, don't struggle anymore. + if err != nil { + return 0, err + } + if failed { + lo = mid + } else { + hi = mid + } + } + return hi, nil +} + +// GetSigners returns the expected signers for a MsgCreateFunToken message. +func (m MsgCreateFunToken) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(m.Sender) + return []sdk.AccAddress{addr} +} + +// ValidateBasic does a sanity check of the provided data +func (m *MsgCreateFunToken) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return fmt.Errorf("invalid sender addr") + } + + emptyBankDenom := m.FromBankDenom == "" + emptyErc20 := m.FromErc20 == nil || m.FromErc20.Size() == 0 + + if emptyErc20 && emptyBankDenom { + return fmt.Errorf("either the \"from_erc20\" or \"from_bank_denom\" must be set") + } + + if !emptyErc20 && !emptyBankDenom { + return fmt.Errorf("either the \"from_erc20\" or \"from_bank_denom\" must be set (but not both)") + } + + return nil +} + +// GetSignBytes implements the LegacyMsg interface. +func (m MsgCreateFunToken) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&m)) +} + +// GetSigners returns the expected signers for a MsgConvertCoinToEvm message. +func (m MsgConvertCoinToEvm) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(m.Sender) + return []sdk.AccAddress{addr} +} + +// ValidateBasic does a sanity check of the provided data +func (m *MsgConvertCoinToEvm) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return fmt.Errorf("invalid sender addr") + } + if m.ToEthAddr.Address.String() == "" || m.ToEthAddr.Size() == 0 { + return fmt.Errorf("empty to_eth_addr") + } + return nil +} + +// GetSignBytes implements the LegacyMsg interface. +func (m MsgConvertCoinToEvm) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&m)) +} diff --git a/x/evm/msg_test.go b/x/evm/msg_test.go new file mode 100644 index 000000000..c57c0f5fd --- /dev/null +++ b/x/evm/msg_test.go @@ -0,0 +1,996 @@ +package evm_test + +import ( + "encoding/json" + "fmt" + "math" + "math/big" + "reflect" + "strings" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/eth/crypto/ethsecp256k1" + "github.com/NibiruChain/nibiru/v2/eth/encoding" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +type MsgsSuite struct { + suite.Suite + + signer keyring.Signer + from common.Address + to common.Address + chainID *big.Int + hundredBigInt *big.Int + + clientCtx client.Context +} + +func TestMsgsSuite(t *testing.T) { + suite.Run(t, new(MsgsSuite)) +} + +func (s *MsgsSuite) SetupTest() { + ethAcc := evmtest.NewEthPrivAcc() + from, privFrom := ethAcc.EthAddr, ethAcc.PrivKey + + s.signer = evmtest.NewSigner(privFrom) + s.from = from + s.to = evmtest.NewEthPrivAcc().EthAddr + s.chainID = big.NewInt(1) + s.hundredBigInt = big.NewInt(100) + + encodingConfig := encoding.MakeConfig(app.ModuleBasics) + s.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) +} + +func (s *MsgsSuite) TestMsgEthereumTx_Constructor() { + evmTx := &evm.EvmTxArgs{ + Nonce: 0, + To: &s.to, + GasLimit: 100000, + Input: []byte("test"), + } + msg := evm.NewTx(evmTx) + + // suite.Require().Equal(msg.Data.To, suite.to.Hex()) + s.Require().Equal(msg.Route(), evm.RouterKey) + // suite.Require().NotNil(msg.To()) + s.Require().Equal(msg.GetMsgs(), []sdk.Msg{msg}) + s.Require().Panics(func() { msg.GetSigners() }) + s.Require().Panics(func() { msg.GetSignBytes() }) + + evmTx2 := &evm.EvmTxArgs{ + Nonce: 0, + GasLimit: 100000, + Input: []byte("test"), + } + msg = evm.NewTx(evmTx2) + s.Require().NotNil(msg) +} + +func (s *MsgsSuite) TestMsgEthereumTx_BuildTx() { + evmTx := &evm.EvmTxArgs{ + Nonce: 0, + To: &s.to, + GasLimit: 100000, + GasPrice: big.NewInt(1), + GasFeeCap: big.NewInt(1), + GasTipCap: big.NewInt(0), + Input: []byte("test"), + } + testCases := []struct { + name string + msg *evm.MsgEthereumTx + expError bool + }{ + { + "build tx - pass", + evm.NewTx(evmTx), + false, + }, + { + "build tx - fail: nil data", + evm.NewTx(evmTx), + true, + }, + } + + for _, tc := range testCases { + if strings.Contains(tc.name, "nil data") { + tc.msg.Data = nil + } + + tx, err := tc.msg.BuildTx(s.clientCtx.TxConfig.NewTxBuilder(), evm.EVMBankDenom) + if tc.expError { + s.Require().Error(err) + } else { + s.Require().NoError(err) + + s.Require().Empty(tx.GetMemo()) + s.Require().Empty(tx.GetTimeoutHeight()) + s.Require().Equal(uint64(100000), tx.GetGas()) + s.Require().Equal(sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdkmath.NewInt(100000))), tx.GetFee()) + } + } +} + +func invalidAddr() string { return "0x0000" } + +func (s *MsgsSuite) TestMsgEthereumTx_ValidateBasic() { + var ( + hundredInt = big.NewInt(100) + validChainID = big.NewInt(9000) + zeroInt = big.NewInt(0) + minusOneInt = big.NewInt(-1) + //nolint:all + exp_2_255 = new(big.Int).Exp(big.NewInt(2), big.NewInt(255), nil) + ) + testCases := []struct { + msg string + to string + amount *big.Int + gasLimit uint64 + gasPrice *big.Int + gasFeeCap *big.Int + gasTipCap *big.Int + from string + accessList *gethcore.AccessList + chainID *big.Int + expectPass bool + errMsg string + }{ + { + msg: "pass with recipient - Legacy Tx", + to: s.to.Hex(), + from: s.from.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: hundredInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: true, + }, + { + msg: "pass with recipient - AccessList Tx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: true, + }, + { + msg: "pass with recipient - DynamicFee Tx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: hundredInt, + gasTipCap: zeroInt, + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: true, + }, + { + msg: "pass contract - Legacy Tx", + to: "", + from: s.from.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: hundredInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: true, + }, + { + msg: "maxInt64 gas limit overflow", + to: s.to.Hex(), + from: s.from.Hex(), + amount: hundredInt, + gasLimit: math.MaxInt64 + 1, + gasPrice: hundredInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: false, + errMsg: "gas limit must be less than math.MaxInt64", + }, + { + msg: "nil amount - Legacy Tx", + to: s.to.Hex(), + from: s.from.Hex(), + amount: nil, + gasLimit: 1000, + gasPrice: hundredInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: true, + }, + { + msg: "negative amount - Legacy Tx", + to: s.to.Hex(), + from: s.from.Hex(), + amount: minusOneInt, + gasLimit: 1000, + gasPrice: hundredInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: false, + errMsg: "amount cannot be negative", + }, + { + msg: "zero gas limit - Legacy Tx", + to: s.to.Hex(), + from: s.from.Hex(), + amount: hundredInt, + gasLimit: 0, + gasPrice: hundredInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: false, + errMsg: "gas limit must not be zero", + }, + { + msg: "nil gas price - Legacy Tx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: nil, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: false, + errMsg: "cannot be nil: invalid gas price", + }, + { + msg: "negative gas price - Legacy Tx", + to: s.to.Hex(), + from: s.from.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: minusOneInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: false, + errMsg: "gas price cannot be negative", + }, + { + msg: "zero gas price - Legacy Tx", + to: s.to.Hex(), + from: s.from.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: true, + }, + { + msg: "invalid from address - Legacy Tx", + to: s.to.Hex(), + from: invalidAddr(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: false, + errMsg: "invalid from address", + }, + { + msg: "out of bound gas fee - Legacy Tx", + to: s.to.Hex(), + from: s.from.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: exp_2_255, + gasFeeCap: nil, + gasTipCap: nil, + chainID: validChainID, + expectPass: false, + errMsg: "out of bound", + }, + { + msg: "nil amount - AccessListTx", + to: s.to.Hex(), + amount: nil, + gasLimit: 1000, + gasPrice: hundredInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: true, + }, + { + msg: "negative amount - AccessListTx", + to: s.to.Hex(), + amount: minusOneInt, + gasLimit: 1000, + gasPrice: hundredInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: false, + errMsg: "amount cannot be negative", + }, + { + msg: "zero gas limit - AccessListTx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 0, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: false, + errMsg: "gas limit must not be zero", + }, + { + msg: "nil gas price - AccessListTx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: nil, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: false, + errMsg: "cannot be nil: invalid gas price", + }, + { + msg: "negative gas price - AccessListTx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: minusOneInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: false, + errMsg: "gas price cannot be negative", + }, + { + msg: "zero gas price - AccessListTx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: true, + }, + { + msg: "invalid from address - AccessListTx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + from: invalidAddr(), + accessList: &gethcore.AccessList{}, + chainID: validChainID, + expectPass: false, + errMsg: "invalid from address", + }, + { + msg: "chain ID not set on AccessListTx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: nil, + expectPass: false, + errMsg: "chain ID must be present on AccessList txs", + }, + { + msg: "nil tx.Data - AccessList Tx", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + expectPass: false, + errMsg: "failed to unpack tx data", + }, + { + msg: "happy, valid chain ID", + to: s.to.Hex(), + amount: hundredInt, + gasLimit: 1000, + gasPrice: zeroInt, + gasFeeCap: nil, + gasTipCap: nil, + accessList: &gethcore.AccessList{}, + chainID: hundredInt, + expectPass: true, + errMsg: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.msg, func() { + to := common.HexToAddress(tc.to) + evmTx := &evm.EvmTxArgs{ + ChainID: tc.chainID, + Nonce: 1, + To: &to, + Amount: tc.amount, + GasLimit: tc.gasLimit, + GasPrice: tc.gasPrice, + GasFeeCap: tc.gasFeeCap, + Accesses: tc.accessList, + } + tx := evm.NewTx(evmTx) + tx.From = tc.from + + // apply nil assignment here to test ValidateBasic function instead of NewTx + if strings.Contains(tc.msg, "nil tx.Data") { + tx.Data = nil + } + + // for legacy_Tx need to sign tx because the chainID is derived + // from signature + if tc.accessList == nil && tc.from == s.from.Hex() { + ethSigner := gethcore.LatestSignerForChainID(tc.chainID) + err := tx.Sign(ethSigner, s.signer) + s.Require().NoError(err) + } + + err := tx.ValidateBasic() + + if tc.expectPass { + s.Require().NoError(err) + } else { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errMsg) + } + }) + } +} + +func (s *MsgsSuite) TestMsgEthereumTx_ValidateBasicAdvanced() { + hundredInt := big.NewInt(100) + evmTx := &evm.EvmTxArgs{ + ChainID: hundredInt, + Nonce: 1, + Amount: big.NewInt(10), + GasLimit: 100000, + GasPrice: big.NewInt(150), + GasFeeCap: big.NewInt(200), + } + + testCases := []struct { + msg string + msgBuilder func() *evm.MsgEthereumTx + expectPass bool + }{ + { + "fails - invalid tx hash", + func() *evm.MsgEthereumTx { + msg := evm.NewTx(evmTx) + msg.Hash = "0x00" + return msg + }, + false, + }, + { + "fails - invalid size", + func() *evm.MsgEthereumTx { + msg := evm.NewTx(evmTx) + msg.Size_ = 1 + return msg + }, + false, + }, + } + + for _, tc := range testCases { + s.Run(tc.msg, func() { + err := tc.msgBuilder().ValidateBasic() + if tc.expectPass { + s.Require().NoError(err) + } else { + s.Require().Error(err) + } + }) + } +} + +func (s *MsgsSuite) TestMsgEthereumTx_Sign() { + testCases := []struct { + msg string + txParams *evm.EvmTxArgs + ethSigner gethcore.Signer + malleate func(tx *evm.MsgEthereumTx) + expectPass bool + }{ + { + "pass - EIP2930 signer", + &evm.EvmTxArgs{ + ChainID: s.chainID, + Nonce: 0, + To: &s.to, + GasLimit: 100000, + Input: []byte("test"), + Accesses: &gethcore.AccessList{}, + }, + gethcore.NewEIP2930Signer(s.chainID), + func(tx *evm.MsgEthereumTx) { tx.From = s.from.Hex() }, + true, + }, + { + "pass - EIP155 signer", + &evm.EvmTxArgs{ + ChainID: s.chainID, + Nonce: 0, + To: &s.to, + GasLimit: 100000, + Input: []byte("test"), + }, + gethcore.NewEIP155Signer(s.chainID), + func(tx *evm.MsgEthereumTx) { tx.From = s.from.Hex() }, + true, + }, + { + "pass - Homestead signer", + &evm.EvmTxArgs{ + ChainID: s.chainID, + Nonce: 0, + To: &s.to, + GasLimit: 100000, + Input: []byte("test"), + }, + gethcore.HomesteadSigner{}, + func(tx *evm.MsgEthereumTx) { tx.From = s.from.Hex() }, + true, + }, + { + "pass - Frontier signer", + &evm.EvmTxArgs{ + ChainID: s.chainID, + Nonce: 0, + To: &s.to, + GasLimit: 100000, + Input: []byte("test"), + }, + gethcore.FrontierSigner{}, + func(tx *evm.MsgEthereumTx) { tx.From = s.from.Hex() }, + true, + }, + { + "no from address ", + &evm.EvmTxArgs{ + ChainID: s.chainID, + Nonce: 0, + To: &s.to, + GasLimit: 100000, + Input: []byte("test"), + Accesses: &gethcore.AccessList{}, + }, + gethcore.NewEIP2930Signer(s.chainID), + func(tx *evm.MsgEthereumTx) { tx.From = "" }, + false, + }, + { + "from address ≠ signer address", + &evm.EvmTxArgs{ + ChainID: s.chainID, + Nonce: 0, + To: &s.to, + GasLimit: 100000, + Input: []byte("test"), + Accesses: &gethcore.AccessList{}, + }, + gethcore.NewEIP2930Signer(s.chainID), + func(tx *evm.MsgEthereumTx) { tx.From = s.to.Hex() }, + false, + }, + } + + for i, tc := range testCases { + tx := evm.NewTx(tc.txParams) + tc.malleate(tx) + err := tx.Sign(tc.ethSigner, s.signer) + if tc.expectPass { + s.Require().NoError(err, "valid test %d failed: %s", i, tc.msg) + + sender, err := tx.GetSender(s.chainID) + s.Require().NoError(err, tc.msg) + s.Require().Equal(tx.From, sender.Hex(), tc.msg) + } else { + s.Require().Error(err, "invalid test %d passed: %s", i, tc.msg) + } + } +} + +func (s *MsgsSuite) TestMsgEthereumTx_Getters() { + evmTx := &evm.EvmTxArgs{ + ChainID: s.chainID, + Nonce: 0, + To: &s.to, + GasLimit: 50, + GasPrice: s.hundredBigInt, + Accesses: &gethcore.AccessList{}, + } + testCases := []struct { + name string + ethSigner gethcore.Signer + exp *big.Int + }{ + { + "get fee - pass", + + gethcore.NewEIP2930Signer(s.chainID), + big.NewInt(5000), + }, + { + "get fee - fail: nil data", + gethcore.NewEIP2930Signer(s.chainID), + nil, + }, + { + "get effective fee - pass", + + gethcore.NewEIP2930Signer(s.chainID), + big.NewInt(5000), + }, + { + "get effective fee - fail: nil data", + gethcore.NewEIP2930Signer(s.chainID), + nil, + }, + { + "get gas - pass", + gethcore.NewEIP2930Signer(s.chainID), + big.NewInt(50), + }, + { + "get gas - fail: nil data", + gethcore.NewEIP2930Signer(s.chainID), + big.NewInt(0), + }, + } + + var fee, effFee *big.Int + for _, tc := range testCases { + tx := evm.NewTx(evmTx) + if strings.Contains(tc.name, "nil data") { + tx.Data = nil + } + switch { + case strings.Contains(tc.name, "get fee"): + fee = tx.GetFee() + s.Require().Equal(tc.exp, fee) + case strings.Contains(tc.name, "get effective fee"): + effFee = tx.EffectiveFeeWei(big.NewInt(0)) + s.Require().Equal(tc.exp, effFee) + case strings.Contains(tc.name, "get gas"): + gas := tx.GetGas() + s.Require().Equal(tc.exp.Uint64(), gas) + } + } +} + +func (s *MsgsSuite) TestFromEthereumTx() { + privkey, _ := ethsecp256k1.GenerateKey() + ethPriv, err := privkey.ToECDSA() + s.Require().NoError(err) + + // 10^80 is more than 256 bits + //nolint:all + exp_10_80 := new(big.Int).Mul(big.NewInt(1), new(big.Int).Exp(big.NewInt(10), big.NewInt(80), nil)) + + testCases := []struct { + msg string + expectPass bool + buildTx func() *gethcore.Transaction + }{ + {"success, normal tx", true, func() *gethcore.Transaction { + tx := gethcore.NewTx(&gethcore.AccessListTx{ + Nonce: 0, + Data: nil, + To: &s.to, + Value: big.NewInt(10), + GasPrice: big.NewInt(1), + Gas: 21000, + }) + tx, err := gethcore.SignTx(tx, gethcore.NewEIP2930Signer(s.chainID), ethPriv) + s.Require().NoError(err) + return tx + }}, + {"success, DynamicFeeTx", true, func() *gethcore.Transaction { + tx := gethcore.NewTx(&gethcore.DynamicFeeTx{ + Nonce: 0, + Data: nil, + To: &s.to, + Value: big.NewInt(10), + Gas: 21000, + }) + tx, err := gethcore.SignTx(tx, gethcore.NewLondonSigner(s.chainID), ethPriv) + s.Require().NoError(err) + return tx + }}, + {"fail, value bigger than 256bits - AccessListTx", false, func() *gethcore.Transaction { + tx := gethcore.NewTx(&gethcore.AccessListTx{ + Nonce: 0, + Data: nil, + To: &s.to, + Value: exp_10_80, + GasPrice: big.NewInt(1), + Gas: 21000, + }) + tx, err := gethcore.SignTx(tx, gethcore.NewEIP2930Signer(s.chainID), ethPriv) + s.Require().NoError(err) + return tx + }}, + {"fail, gas price bigger than 256bits - AccessListTx", false, func() *gethcore.Transaction { + tx := gethcore.NewTx(&gethcore.AccessListTx{ + Nonce: 0, + Data: nil, + To: &s.to, + Value: big.NewInt(1), + GasPrice: exp_10_80, + Gas: 21000, + }) + tx, err := gethcore.SignTx(tx, gethcore.NewEIP2930Signer(s.chainID), ethPriv) + s.Require().NoError(err) + return tx + }}, + {"fail, value bigger than 256bits - LegacyTx", false, func() *gethcore.Transaction { + tx := gethcore.NewTx(&gethcore.LegacyTx{ + Nonce: 0, + Data: nil, + To: &s.to, + Value: exp_10_80, + GasPrice: big.NewInt(1), + Gas: 21000, + }) + tx, err := gethcore.SignTx(tx, gethcore.NewEIP2930Signer(s.chainID), ethPriv) + s.Require().NoError(err) + return tx + }}, + {"fail, gas price bigger than 256bits - LegacyTx", false, func() *gethcore.Transaction { + tx := gethcore.NewTx(&gethcore.LegacyTx{ + Nonce: 0, + Data: nil, + To: &s.to, + Value: big.NewInt(1), + GasPrice: exp_10_80, + Gas: 21000, + }) + tx, err := gethcore.SignTx(tx, gethcore.NewEIP2930Signer(s.chainID), ethPriv) + s.Require().NoError(err) + return tx + }}, + } + + for _, tc := range testCases { + ethTx := tc.buildTx() + tx := &evm.MsgEthereumTx{} + err := tx.FromEthereumTx(ethTx) + if tc.expectPass { + s.Require().NoError(err) + + // round-trip test + s.Require().NoError(assertEqualTxs(tx.AsTransaction(), ethTx)) + } else { + s.Require().Error(err) + } + } +} + +// TestTxEncoding tests serializing/de-serializing to/from rlp and JSON. +// adapted from go-ethereum +func (s *MsgsSuite) TestTxEncoding() { + key, err := crypto.GenerateKey() + if err != nil { + s.T().Fatalf("could not generate key: %v", err) + } + var ( + signer = gethcore.NewEIP2930Signer(common.Big1) + addr = common.HexToAddress("0x0000000000000000000000000000000000000001") + recipient = common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") + accesses = gethcore.AccessList{{Address: addr, StorageKeys: []common.Hash{{0}}}} + ) + for i := uint64(0); i < 500; i++ { + var txdata gethcore.TxData + switch i % 5 { + case 0: + // Legacy tx. + txdata = &gethcore.LegacyTx{ + Nonce: i, + To: &recipient, + Gas: 1, + GasPrice: big.NewInt(2), + Data: []byte("abcdef"), + } + case 1: + // Legacy tx contract creation. + txdata = &gethcore.LegacyTx{ + Nonce: i, + Gas: 1, + GasPrice: big.NewInt(2), + Data: []byte("abcdef"), + } + case 2: + // Tx with non-zero access list. + txdata = &gethcore.AccessListTx{ + ChainID: big.NewInt(1), + Nonce: i, + To: &recipient, + Gas: 123457, + GasPrice: big.NewInt(10), + AccessList: accesses, + Data: []byte("abcdef"), + } + case 3: + // Tx with empty access list. + txdata = &gethcore.AccessListTx{ + ChainID: big.NewInt(1), + Nonce: i, + To: &recipient, + Gas: 123457, + GasPrice: big.NewInt(10), + Data: []byte("abcdef"), + } + case 4: + // Contract creation with access list. + txdata = &gethcore.AccessListTx{ + ChainID: big.NewInt(1), + Nonce: i, + Gas: 123457, + GasPrice: big.NewInt(10), + AccessList: accesses, + } + } + tx, err := gethcore.SignNewTx(key, signer, txdata) + if err != nil { + s.T().Fatalf("could not sign transaction: %v", err) + } + // RLP + parsedTx, err := encodeDecodeBinary(tx) + if err != nil { + s.T().Fatal(err) + } + err = assertEqualTxs(parsedTx.AsTransaction(), tx) + s.Require().NoError(err) + } +} + +func encodeDecodeBinary(tx *gethcore.Transaction) (*evm.MsgEthereumTx, error) { + data, err := tx.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("rlp encoding failed: %v", err) + } + parsedTx := &evm.MsgEthereumTx{} + if err := parsedTx.UnmarshalBinary(data); err != nil { + return nil, fmt.Errorf("rlp decoding failed: %v", err) + } + return parsedTx, nil +} + +func assertEqualTxs(orig *gethcore.Transaction, cpy *gethcore.Transaction) error { + // compare nonce, price, gaslimit, recipient, amount, payload, V, R, S + if want, got := orig.Hash(), cpy.Hash(); want != got { + return fmt.Errorf("parsed tx differs from original tx, want %v, got %v", want, got) + } + if want, got := orig.ChainId(), cpy.ChainId(); want.Cmp(got) != 0 { + return fmt.Errorf("invalid chain id, want %d, got %d", want, got) + } + if orig.AccessList() != nil { + if !reflect.DeepEqual(orig.AccessList(), cpy.AccessList()) { + return fmt.Errorf("access list wrong") + } + } + return nil +} + +func (s *MsgsSuite) TestUnwrapEthererumMsg() { + _, err := evm.UnwrapEthereumMsg(nil, common.Hash{}) + s.NotNil(err) + + encodingConfig := encoding.MakeConfig(app.ModuleBasics) + clientCtx := client.Context{}.WithTxConfig(encodingConfig.TxConfig) + builder, _ := clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder) + + tx := builder.GetTx().(sdk.Tx) + _, err = evm.UnwrapEthereumMsg(&tx, common.Hash{}) + s.NotNil(err) + + evmTxParams := &evm.EvmTxArgs{ + ChainID: big.NewInt(1), + Nonce: 0, + To: &common.Address{}, + Amount: big.NewInt(0), + GasLimit: 0, + GasPrice: big.NewInt(0), + Input: []byte{}, + } + + msg := evm.NewTx(evmTxParams) + err = builder.SetMsgs(msg) + s.Nil(err) + + tx = builder.GetTx().(sdk.Tx) + unwrappedMsg, err := evm.UnwrapEthereumMsg(&tx, msg.AsTransaction().Hash()) + s.Nil(err) + s.Equal(unwrappedMsg, msg) +} + +func (s *MsgsSuite) TestMarshalJSON() { + addrHex := "0x1111111111111111122222222222222222222222" + { + jsonBz, err := json.Marshal(addrHex) + s.NoError(err) + eip55Addr := new(eth.EIP55Addr) + s.Require().NoError(eip55Addr.UnmarshalJSON(jsonBz)) + s.Require().Equal(addrHex, eip55Addr.Hex()) + } + + sender := "nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl" + { + fromErc20, err := eth.NewEIP55AddrFromStr(addrHex) + s.NoError(err) + goType := evm.MsgCreateFunToken{ + FromErc20: &fromErc20, + Sender: sender, + } + + outJsonBz, err := json.Marshal(goType) + s.Require().NoError(err) + + var outGoType evm.MsgCreateFunToken + err = json.Unmarshal(outJsonBz, &outGoType) + s.NoError(err) + s.Equal(goType, outGoType) + } + + var goType evm.MsgCreateFunToken + err := json.Unmarshal([]byte(` + { + "from_erc20": "0x1111111111111111122222222222222222222222", + "sender": "nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl" + } + `), &goType) + s.NoError(err) + s.Equal(addrHex, goType.FromErc20.Hex()) + s.Equal(sender, goType.Sender) +} diff --git a/x/evm/params.go b/x/evm/params.go new file mode 100644 index 000000000..e0211d0bb --- /dev/null +++ b/x/evm/params.go @@ -0,0 +1,99 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + + "github.com/ethereum/go-ethereum/core/vm" + "golang.org/x/exp/slices" + + "github.com/NibiruChain/nibiru/v2/app/appconst" +) + +const ( + // EVMBankDenom specifies the bank denomination for the asset used to run EVM + // state transitions as the analog to "ether". 1 ether in solidity means 1 + // NIBI on Nibru EVM, implying that the EVMBankDenom is "unibi", the coin + // base of the NIBI token. + EVMBankDenom = appconst.BondDenom +) + +// DefaultParams returns default evm parameters +// ExtraEIPs is empty to prevent overriding the latest hard fork instruction set +func DefaultParams() Params { + return Params{ + ExtraEIPs: []int64{}, + // EVMChannels: Unused but intended for use with future IBC functionality + EVMChannels: []string{}, + CreateFuntokenFee: math.NewIntWithDecimal(10_000, 6), // 10_000 NIBI + } +} + +// validateChannels checks if channels ids are valid +func validateChannels(i any) error { + channels, ok := i.([]string) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + for _, channel := range channels { + if err := host.ChannelIdentifierValidator(channel); err != nil { + return errorsmod.Wrap( + channeltypes.ErrInvalidChannelIdentifier, err.Error(), + ) + } + } + + return nil +} + +// Validate performs basic validation on evm parameters. +func (p Params) Validate() error { + if err := validateEIPs(p.ExtraEIPs); err != nil { + return err + } + + return validateChannels(p.EVMChannels) +} + +// EIPs returns the ExtraEIPS as a int slice +func (p Params) EIPs() []int { + eips := make([]int, len(p.ExtraEIPs)) + for i, eip := range p.ExtraEIPs { + eips[i] = int(eip) + } + return eips +} + +// IsEVMChannel returns true if the channel provided is in the list of +// EVM channels +func (p Params) IsEVMChannel(channel string) bool { + return slices.Contains(p.EVMChannels, channel) +} + +func validateEIPs(i any) error { + eips, ok := i.([]int64) + if !ok { + return fmt.Errorf("invalid EIP slice type: %T", i) + } + + uniqueEIPs := make(map[int64]struct{}) + + for _, eip := range eips { + if !vm.ValidEip(int(eip)) { + return fmt.Errorf("EIP %d is not activateable, valid EIPs are: %s", eip, vm.ActivateableEips()) + } + + if _, ok := uniqueEIPs[eip]; ok { + return fmt.Errorf("found duplicate EIP: %d", eip) + } + uniqueEIPs[eip] = struct{}{} + } + + return nil +} diff --git a/x/evm/precompile/README.md b/x/evm/precompile/README.md new file mode 100644 index 000000000..d04248dab --- /dev/null +++ b/x/evm/precompile/README.md @@ -0,0 +1,13 @@ +# Sample Hardhat Project + +This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a Hardhat Ignition module that deploys that contract. + +Try running some of the following tasks: + +```shell +npx hardhat help +npx hardhat test +REPORT_GAS=true npx hardhat test +npx hardhat node +npx hardhat ignition deploy ./ignition/modules/Lock.js +``` diff --git a/x/evm/precompile/errors.go b/x/evm/precompile/errors.go new file mode 100644 index 000000000..eadd99e24 --- /dev/null +++ b/x/evm/precompile/errors.go @@ -0,0 +1,66 @@ +package precompile + +import ( + "fmt" + "reflect" + + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/core/vm" +) + +func assertNotReadonlyTx(readOnly bool, method *gethabi.Method) error { + if readOnly { + return fmt.Errorf("method %s cannot be called in a read-only context (e.g. staticcall)", method.Name) + } + return nil +} + +// ErrPrecompileRun is error function intended for use in a `defer` pattern, +// which modifies the input error in the event that its value becomes non-nil. +// This creates a concise way to prepend extra information to the original error. +func ErrPrecompileRun(err error, p vm.PrecompiledContract) error { + if err != nil { + precompileType := reflect.TypeOf(p).Name() + err = fmt.Errorf("precompile error: failed to run %s: %w", precompileType, err) + } + return err +} + +// Error short-hand for type validation +func ErrArgTypeValidation(solidityHint string, arg any) error { + return fmt.Errorf("type validation failed for (%s) argument: %s", solidityHint, arg) +} + +// Error when parsing method arguments +func ErrInvalidArgs(err error) error { + return fmt.Errorf("invalid method args: %w", err) +} + +func ErrMethodCalled(method *gethabi.Method, wrapped error) error { + return fmt.Errorf("%s method called: %w", method.Name, wrapped) +} + +// assertContractQuery checks if a contract call is a valid query operation. This +// function verifies that no funds (wei) are being sent with the query, as query +// operations should be read-only and not involve any value transfer. +func assertContractQuery(contract *vm.Contract) error { + weiValue := contract.Value() + if weiValue != nil && weiValue.Sign() != 0 { + return fmt.Errorf( + "funds (wei value) must not be expended calling a query function; received wei value %s", weiValue, + ) + } + + return nil +} + +// assertNumArgs checks if the number of provided arguments matches the expected +// count. If lenArgs does not equal wantArgsLen, it returns an error describing +// the mismatch between expected and actual argument counts. +func assertNumArgs(args []any, wantArgsLen int) error { + lenArgs := len(args) + if lenArgs != wantArgsLen { + return fmt.Errorf("expected %d arguments but got %d; args: %v", wantArgsLen, lenArgs, args) + } + return nil +} diff --git a/x/evm/precompile/funtoken.go b/x/evm/precompile/funtoken.go new file mode 100644 index 000000000..82739c612 --- /dev/null +++ b/x/evm/precompile/funtoken.go @@ -0,0 +1,765 @@ +package precompile + +import ( + "fmt" + "math/big" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/app/keepers" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" +) + +var _ vm.PrecompiledContract = (*precompileFunToken)(nil) + +// Precompile address for "IFunToken.sol", the contract that +// enables transfers of ERC20 tokens to "nibi" addresses as bank coins +// using the ERC20's `FunToken` mapping. +var PrecompileAddr_FunToken = gethcommon.HexToAddress("0x0000000000000000000000000000000000000800") + +func (p precompileFunToken) Address() gethcommon.Address { + return PrecompileAddr_FunToken +} + +// RequiredGas calculates the cost of calling the precompile in gas units. +func (p precompileFunToken) RequiredGas(input []byte) (gasCost uint64) { + return requiredGas(input, p.ABI()) +} + +func (p precompileFunToken) ABI() *gethabi.ABI { + return embeds.SmartContract_FunToken.ABI +} + +const ( + FunTokenMethod_sendToBank PrecompileMethod = "sendToBank" + FunTokenMethod_balance PrecompileMethod = "balance" + FunTokenMethod_bankBalance PrecompileMethod = "bankBalance" + FunTokenMethod_whoAmI PrecompileMethod = "whoAmI" + FunTokenMethod_sendToEvm PrecompileMethod = "sendToEvm" + FunTokenMethod_bankMsgSend PrecompileMethod = "bankMsgSend" +) + +// Run runs the precompiled contract +func (p precompileFunToken) Run( + evm *vm.EVM, contract *vm.Contract, readonly bool, +) (bz []byte, err error) { + defer func() { + err = ErrPrecompileRun(err, p) + }() + startResult, err := OnRunStart(evm, contract.Input, p.ABI(), contract.Gas) + if err != nil { + return nil, err + } + + // Gracefully handles "out of gas" + defer HandleOutOfGasPanic(&err)() + + abciEventsStartIdx := len(startResult.CacheCtx.EventManager().Events()) + + method := startResult.Method + switch PrecompileMethod(method.Name) { + case FunTokenMethod_sendToBank: + bz, err = p.sendToBank(startResult, contract.CallerAddress, readonly, evm) + case FunTokenMethod_balance: + bz, err = p.balance(startResult, contract, evm) + case FunTokenMethod_bankBalance: + bz, err = p.bankBalance(startResult, contract) + case FunTokenMethod_whoAmI: + bz, err = p.whoAmI(startResult, contract) + case FunTokenMethod_sendToEvm: + bz, err = p.sendToEvm(startResult, contract.CallerAddress, readonly, evm) + case FunTokenMethod_bankMsgSend: + bz, err = p.bankMsgSend(startResult, contract.CallerAddress, readonly) + default: + // Note that this code path should be impossible to reach since + // "[decomposeInput]" parses methods directly from the ABI. + err = fmt.Errorf("invalid method called with name \"%s\"", method.Name) + return + } + // Gas consumed by a local gas meter + contract.UseGas(startResult.CacheCtx.GasMeter().GasConsumed()) + if err != nil { + return nil, err + } + + // Emit extra events for the EVM if this is a transaction + // https://github.com/NibiruChain/nibiru/issues/2121 + if isMutation[PrecompileMethod(startResult.Method.Name)] { + EmitEventAbciEvents( + startResult.CacheCtx, + startResult.StateDB, + startResult.CacheCtx.EventManager().Events()[abciEventsStartIdx:], + p.Address(), + ) + } + + return bz, err +} + +func PrecompileFunToken(keepers keepers.PublicKeepers) vm.PrecompiledContract { + return precompileFunToken{ + evmKeeper: keepers.EvmKeeper, + } +} + +type precompileFunToken struct { + evmKeeper *evmkeeper.Keeper +} + +// sendToBank: Implements "IFunToken.sendToBank" +// +// The "args" populate the following function signature in Solidity: +// +// ```solidity +// /// @dev sendToBank sends ERC20 tokens as coins to a Nibiru base account +// /// @param erc20 the address of the ERC20 token contract +// /// @param amount the amount of tokens to send +// /// @param to the receiving Nibiru base account address as a string +// function sendToBank(address erc20, uint256 amount, string memory to) external; +// ``` +// +// Because [sendToBank] uses "SendCoinsFromModuleToAccount" to send Bank Coins, +// this method correctly avoids sending funds to addresses blocked by the Bank +// module. +func (p precompileFunToken) sendToBank( + startResult OnRunStartResult, + caller gethcommon.Address, + readOnly bool, + evmObj *vm.EVM, +) (bz []byte, err error) { + ctx, method, args := startResult.CacheCtx, startResult.Method, startResult.Args + if err := assertNotReadonlyTx(readOnly, method); err != nil { + return nil, err + } + + erc20, amount, to, err := p.parseArgsSendToBank(args) + if err != nil { + return nil, ErrInvalidArgs(err) + } + + var evmResponses []*evm.MsgEthereumTxResponse + + // ERC20 must have FunToken mapping + funtokens := p.evmKeeper.FunTokens.Collect( + ctx, p.evmKeeper.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20), + ) + if len(funtokens) != 1 { + err = fmt.Errorf("no FunToken mapping exists for ERC20 \"%s\"", erc20.Hex()) + return + } + funtoken := funtokens[0] + + // Amount should be positive + if amount == nil || amount.Cmp(big.NewInt(0)) != 1 { + return nil, fmt.Errorf("transfer amount must be positive") + } + + // The "to" argument must be a valid Nibiru address + toAddr, err := sdk.AccAddressFromBech32(to) + if err != nil { + return nil, fmt.Errorf("\"to\" is not a valid address (%s): %w", to, err) + } + + // Caller transfers ERC20 to the EVM module account + gotAmount, transferResp, err := p.evmKeeper.ERC20().Transfer( + erc20, /*erc20*/ + caller, /*from*/ + evm.EVM_MODULE_ADDRESS, /*to*/ + amount, /*value*/ + ctx, + evmObj, + ) + if err != nil { + return nil, fmt.Errorf("error in ERC20.transfer from caller to EVM account: %w", err) + } + evmResponses = append(evmResponses, transferResp) + + // EVM account mints FunToken.BankDenom to module account + coinToSend := sdk.NewCoin(funtoken.BankDenom, math.NewIntFromBigInt(gotAmount)) + if funtoken.IsMadeFromCoin { + // If the FunToken mapping was created from a bank coin, then the EVM account + // owns the ERC20 contract and was the original minter of the ERC20 tokens. + // Since we're sending them away and want accurate total supply tracking, the + // tokens need to be burned. + burnResp, err := p.evmKeeper.ERC20().Burn(erc20, evm.EVM_MODULE_ADDRESS, gotAmount, ctx, evmObj) + if err != nil { + return nil, fmt.Errorf("ERC20.Burn: %w", err) + } + evmResponses = append(evmResponses, burnResp) + } else { + // NOTE: The NibiruBankKeeper needs to reference the current [vm.StateDB] before + // any operation that has the potential to use Bank send methods. This will + // guarantee that [evmkeeper.Keeper.SetAccBalance] journal changes are + // recorded if wei (NIBI) is transferred. + err = p.evmKeeper.Bank.MintCoins(ctx, evm.ModuleName, sdk.NewCoins(coinToSend)) + if err != nil { + return nil, fmt.Errorf("mint failed for module \"%s\" (%s): contract caller %s: %w", + evm.ModuleName, evm.EVM_MODULE_ADDRESS.Hex(), caller.Hex(), err, + ) + } + } + + // Transfer the bank coin + // + // NOTE: The NibiruBankKeeper needs to reference the current [vm.StateDB] before + // any operation that has the potential to use Bank send methods. This will + // guarantee that [evmkeeper.Keeper.SetAccBalance] journal changes are + // recorded if wei (NIBI) is transferred. + err = p.evmKeeper.Bank.SendCoinsFromModuleToAccount( + ctx, + evm.ModuleName, + toAddr, + sdk.NewCoins(coinToSend), + ) + if err != nil { + return nil, fmt.Errorf("send failed for module \"%s\" (%s): contract caller %s: %w", + evm.ModuleName, evm.EVM_MODULE_ADDRESS.Hex(), caller.Hex(), err, + ) + } + for _, resp := range evmResponses { + for _, log := range resp.Logs { + startResult.StateDB.AddLog(log.ToEthereum()) + } + } + + // TODO: UD-DEBUG: feat: Emit EVM events + // https://github.com/NibiruChain/nibiru/issues/2121 + // TODO: emit event for balance change of sender + // TODO: emit event for balance change of recipient + + return method.Outputs.Pack(gotAmount) +} + +func (p precompileFunToken) parseArgsSendToBank(args []any) ( + erc20 gethcommon.Address, + amount *big.Int, + to string, + err error, +) { + if e := assertNumArgs(args, 3); e != nil { + err = e + return + } + + argIdx := 0 + erc20, ok := args[argIdx].(gethcommon.Address) + if !ok { + err = ErrArgTypeValidation("address erc20", args[argIdx]) + return + } + + argIdx++ + amount, ok = args[argIdx].(*big.Int) + if !ok { + err = ErrArgTypeValidation("uint256 amount", args[argIdx]) + return + } + + argIdx++ + to, ok = args[argIdx].(string) + if !ok { + err = ErrArgTypeValidation("string to", args[argIdx]) + return + } + + return +} + +// balance: Implements "IFunToken.balance" +// +// The "args" populate the following function signature in Solidity: +// +// ```solidity +// function balance( +// address who, +// address funtoken +// ) +// external +// returns ( +// uint256 erc20Balance, +// uint256 bankBalance, +// FunToken memory token, +// NibiruAccount memory whoAddrs +// ); +// ``` +func (p precompileFunToken) balance( + start OnRunStartResult, + contract *vm.Contract, + evmObj *vm.EVM, +) (bz []byte, err error) { + method, args, ctx := start.Method, start.Args, start.CacheCtx + defer func() { + if err != nil { + err = ErrMethodCalled(method, err) + } + }() + if err := assertContractQuery(contract); err != nil { + return bz, err + } + + addrEth, addrBech32, funtoken, err := p.parseArgsBalance(args, ctx) + if err != nil { + err = ErrInvalidArgs(err) + return + } + + erc20Bal, err := p.evmKeeper.ERC20().BalanceOf(funtoken.Erc20Addr.Address, addrEth, ctx, evmObj) + if err != nil { + return + } + bankBal := p.evmKeeper.Bank.GetBalance(ctx, addrBech32, funtoken.BankDenom).Amount.BigInt() + + return method.Outputs.Pack([]any{ + erc20Bal, + bankBal, + struct { + Erc20 gethcommon.Address `json:"erc20"` + BankDenom string `json:"bankDenom"` + }{ + Erc20: funtoken.Erc20Addr.Address, + BankDenom: funtoken.BankDenom, + }, + struct { + EthAddr gethcommon.Address `json:"ethAddr"` + Bech32Addr string `json:"bech32Addr"` + }{ + EthAddr: addrEth, + Bech32Addr: addrBech32.String(), + }, + }...) +} + +func (p precompileFunToken) parseArgsBalance(args []any, ctx sdk.Context) ( + addrEth gethcommon.Address, + addrBech32 sdk.AccAddress, + funtoken evm.FunToken, + err error, +) { + if e := assertNumArgs(args, 2); e != nil { + err = e + return + } + + argIdx := 0 + who, ok := args[argIdx].(gethcommon.Address) + if !ok { + err = ErrArgTypeValidation("bytes who", args[argIdx]) + return + } + req := &evm.QueryEthAccountRequest{Address: who.Hex()} + _, e := req.Validate() + if e != nil { + err = e + return + } + addrEth = gethcommon.HexToAddress(req.Address) + addrBech32 = eth.EthAddrToNibiruAddr(addrEth) + + argIdx++ + funtokenErc20, ok := args[argIdx].(gethcommon.Address) + if !ok { + err = ErrArgTypeValidation("bytes funtoken", args[argIdx]) + return + } + resp, e := p.evmKeeper.FunTokenMapping(ctx, &evm.QueryFunTokenMappingRequest{ + Token: funtokenErc20.Hex(), + }) + if e != nil { + err = e + return + } + + return addrEth, addrBech32, *resp.FunToken, nil +} + +// bankBalance: Implements "IFunToken.bankBalance" +// +// The "args" populate the following function signature in Solidity: +// +// ```solidity +// function bankBalance( +// address who, +// string calldata bankDenom +// ) external returns (uint256 bankBalance, NibiruAccount memory whoAddrs); +// ``` +func (p precompileFunToken) bankBalance( + start OnRunStartResult, + contract *vm.Contract, +) (bz []byte, err error) { + method, args, ctx := start.Method, start.Args, start.CacheCtx + defer func() { + if err != nil { + err = ErrMethodCalled(method, err) + } + }() + if err := assertContractQuery(contract); err != nil { + return bz, err + } + + addrEth, addrBech32, bankDenom, err := p.parseArgsBankBalance(args) + if err != nil { + err = ErrInvalidArgs(err) + return + } + bankBal := p.evmKeeper.Bank.GetBalance(ctx, addrBech32, bankDenom).Amount.BigInt() + + return method.Outputs.Pack([]any{ + bankBal, + struct { + EthAddr gethcommon.Address `json:"ethAddr"` + Bech32Addr string `json:"bech32Addr"` + }{ + EthAddr: addrEth, + Bech32Addr: addrBech32.String(), + }, + }...) +} + +func (p precompileFunToken) parseArgsBankBalance(args []any) ( + addrEth gethcommon.Address, + addrBech32 sdk.AccAddress, + bankDenom string, + err error, +) { + if e := assertNumArgs(args, 2); e != nil { + err = e + return + } + + argIdx := 0 + who, ok := args[argIdx].(gethcommon.Address) + if !ok { + err = ErrArgTypeValidation("bytes who", args[argIdx]) + return + } + req := &evm.QueryEthAccountRequest{Address: who.Hex()} + _, e := req.Validate() + if e != nil { + err = e + return + } + addrEth = gethcommon.HexToAddress(req.Address) + addrBech32 = eth.EthAddrToNibiruAddr(addrEth) + + argIdx++ + bankDenom, ok = args[argIdx].(string) + if !ok { + err = ErrArgTypeValidation("string bankDenom", args[argIdx]) + return + } + if e := sdk.ValidateDenom(bankDenom); e != nil { + err = e + return + } + + return addrEth, addrBech32, bankDenom, nil +} + +// whoAmI: Implements "IFunToken.whoAmI" +// +// The "args" populate the following function signature in Solidity: +// +// ```solidity +// function whoAmI( +// string calldata who +// ) external returns (NibiruAccount memory whoAddrs); +// ``` +func (p precompileFunToken) whoAmI( + start OnRunStartResult, + contract *vm.Contract, +) (bz []byte, err error) { + method, args := start.Method, start.Args + defer func() { + if err != nil { + err = ErrMethodCalled(method, err) + } + }() + if err := assertContractQuery(contract); err != nil { + return bz, err + } + + addrEth, addrBech32, err := p.parseArgsWhoAmI(args) + if err != nil { + err = ErrInvalidArgs(err) + return + } + + return method.Outputs.Pack([]any{ + struct { + EthAddr gethcommon.Address `json:"ethAddr"` + Bech32Addr string `json:"bech32Addr"` + }{ + EthAddr: addrEth, + Bech32Addr: addrBech32.String(), + }, + }...) +} + +func (p precompileFunToken) parseArgsWhoAmI(args []any) ( + addrEth gethcommon.Address, + addrBech32 sdk.AccAddress, + err error, +) { + if e := assertNumArgs(args, 1); e != nil { + err = e + return + } + + argIdx := 0 + who, ok := args[argIdx].(string) + if !ok { + err = ErrArgTypeValidation("string calldata who", args[argIdx]) + return + } + req := &evm.QueryEthAccountRequest{Address: who} + isBech32, e := req.Validate() + if e != nil { + err = e + return + } + if isBech32 { + addrBech32 = sdk.MustAccAddressFromBech32(req.Address) + addrEth = eth.NibiruAddrToEthAddr(addrBech32) + } else { + addrEth = gethcommon.HexToAddress(req.Address) + addrBech32 = eth.EthAddrToNibiruAddr(addrEth) + } + return addrEth, addrBech32, nil +} + +// SendToEvm: Implements "IFunToken.sendToEvm" +// Transfers the caller's bank coin `denom` to its ERC-20 representation on +// the EVM side. +func (p precompileFunToken) sendToEvm( + startResult OnRunStartResult, + caller gethcommon.Address, + readOnly bool, + evmObj *vm.EVM, +) ([]byte, error) { + ctx, method, args := startResult.CacheCtx, startResult.Method, startResult.Args + if err := assertNotReadonlyTx(readOnly, method); err != nil { + return nil, err + } + // parse call: (string bankDenom, uint256 amount, string to) + bankDenom, amount, toStr, err := parseArgsSendToEvm(args) + if err != nil { + return nil, ErrInvalidArgs(err) + } + + // load the FunToken mapping + // For bankDenom, check if there's an existing funtoken + funtokens := p.evmKeeper.FunTokens.Collect( + ctx, p.evmKeeper.FunTokens.Indexes.BankDenom.ExactMatch(ctx, bankDenom), + ) + if len(funtokens) == 0 { + return nil, fmt.Errorf("no funtoken found for bank denom \"%s\"", bankDenom) + } + funtoken := funtokens[0] + + if amount == nil || amount.Sign() != 1 { + return nil, fmt.Errorf("transfer amount must be positive") + } + + // parse the `to` argument as hex or bech32 address + toEthAddr, err := parseToAddr(toStr) + if err != nil { + return nil, fmt.Errorf("recipient address invalid: %w", err) + } + + // 1) remove (burn or escrow) the bank coin from caller + coinToSend := sdk.NewCoin(funtoken.BankDenom, math.NewIntFromBigInt(amount)) + senderBech32 := eth.EthAddrToNibiruAddr(caller) + + // bank send from account => module + if err := p.evmKeeper.Bank.SendCoinsFromAccountToModule( + ctx, senderBech32, evm.ModuleName, sdk.NewCoins(coinToSend), + ); err != nil { + return nil, fmt.Errorf("failed to send coins to module: %w", err) + } + + // 2) mint (or unescrow) the ERC20 + erc20Addr := funtoken.Erc20Addr.Address + actualAmt, err := p.mintOrUnescrowERC20( + ctx, + erc20Addr, /*erc20Contract*/ + toEthAddr, /*to*/ + coinToSend.Amount.BigInt(), /*amount*/ + funtoken, /*funtoken*/ + evmObj, + ) + if err != nil { + return nil, err + } + + if !funtoken.IsMadeFromCoin { + // If the tokens is from an ERC20, we need to burn the cosmos coin + // and unescrow the ERC20 tokens to the recipient. + err := p.evmKeeper.Bank.BurnCoins(ctx, evm.ModuleName, sdk.NewCoins(coinToSend)) + if err != nil { + return nil, fmt.Errorf("failed to burn coins: %w", err) + } + } + + // return the number of tokens minted + return method.Outputs.Pack(actualAmt) +} + +func (p precompileFunToken) mintOrUnescrowERC20( + ctx sdk.Context, + erc20Addr gethcommon.Address, + to gethcommon.Address, + amount *big.Int, + funtoken evm.FunToken, + evmObj *vm.EVM, +) (*big.Int, error) { + // If funtoken is "IsMadeFromCoin", we own the ERC20 contract, so we can mint. + // If not, we do a transfer from EVM module to 'to' address using escrowed tokens. + if funtoken.IsMadeFromCoin { + _, err := p.evmKeeper.ERC20().Mint( + erc20Addr, /*erc20Contract*/ + evm.EVM_MODULE_ADDRESS, /*from*/ + to, /*to*/ + amount, + ctx, + evmObj, + ) + if err != nil { + return nil, fmt.Errorf("mint erc20 error: %w", err) + } + // For an owner-minted contract, the entire `amount` is minted. + return amount, nil + } else { + balanceIncrease, _, err := p.evmKeeper.ERC20().Transfer( + erc20Addr, evm.EVM_MODULE_ADDRESS, to, amount, ctx, evmObj, + ) + if err != nil { + return nil, fmt.Errorf("erc20.transfer from module to user: %w", err) + } + return balanceIncrease, nil + } +} + +// parse the arguments: (string bankDenom, uint256 amount, string to) +func parseArgsSendToEvm(args []any) (bankDenom string, amount *big.Int, to string, err error) { + if e := assertNumArgs(args, 3); e != nil { + return "", nil, "", e + } + var ok bool + // 0) bankDenom + bankDenom, ok = args[0].(string) + if !ok { + err = ErrArgTypeValidation("string bankDenom", args[0]) + return + } + // 1) amount + amount, ok = args[1].(*big.Int) + if !ok { + err = ErrArgTypeValidation("uint256 amount", args[1]) + return + } + // 2) to + to, ok = args[2].(string) + if !ok { + err = ErrArgTypeValidation("string to", args[2]) + return + } + return +} + +// parse a user-specified address "toStr" that might be bech32 or hex +func parseToAddr(toStr string) (gethcommon.Address, error) { + // check if bech32 or hex + if err := eth.ValidateAddress(toStr); err == nil { + // hex address + return gethcommon.HexToAddress(toStr), nil + } + // else try bech32 + nibAddr, e := sdk.AccAddressFromBech32(toStr) + if e != nil { + return gethcommon.Address{}, fmt.Errorf("invalid bech32 or hex address: %w", e) + } + return eth.NibiruAddrToEthAddr(nibAddr), nil +} + +func (p precompileFunToken) bankMsgSend( + startResult OnRunStartResult, + caller gethcommon.Address, + readOnly bool, +) ([]byte, error) { + ctx, method, args := startResult.CacheCtx, startResult.Method, startResult.Args + if err := assertNotReadonlyTx(readOnly, method); err != nil { + return nil, err + } + + // parse call: (string to, string denom, uint256 amount) + toStr, denom, amount, err := parseArgsBankMsgSend(args) + if err != nil { + return nil, ErrInvalidArgs(err) + } + + // parse toStr (bech32 or hex) + toEthAddr, e := parseToAddr(toStr) + if e != nil { + return nil, e + } + fromBech32 := eth.EthAddrToNibiruAddr(caller) + toBech32 := eth.EthAddrToNibiruAddr(toEthAddr) + + // do the bank send + coin := sdk.NewCoins(sdk.NewCoin(denom, math.NewIntFromBigInt(amount))) + bankMsg := &bank.MsgSend{ + FromAddress: fromBech32.String(), + ToAddress: toBech32.String(), + Amount: coin, + } + if err := bankMsg.ValidateBasic(); err != nil { + return nil, err + } + if _, err := bankkeeper.NewMsgServerImpl(p.evmKeeper.Bank).Send( + sdk.WrapSDKContext(ctx), bankMsg, + ); err != nil { + return nil, fmt.Errorf("bankMsgSend: %w", err) + } + // Return bool success + return method.Outputs.Pack(true) +} + +func parseArgsBankMsgSend(args []any) (toStr, denom string, amount *big.Int, err error) { + if e := assertNumArgs(args, 3); e != nil { + err = e + return + } + // 0) to + var ok bool + toStr, ok = args[0].(string) + if !ok { + err = ErrArgTypeValidation("string to", args[0]) + return + } + + // 1) denom + denom, ok = args[1].(string) + if !ok { + err = ErrArgTypeValidation("string bankDenom", args[1]) + return + } + + // 2) amount + tmpAmount, ok := args[2].(*big.Int) + if !ok { + err = ErrArgTypeValidation("uint256 amount", args[2]) + return + } + amount = tmpAmount + + return +} diff --git a/x/evm/precompile/funtoken_test.go b/x/evm/precompile/funtoken_test.go new file mode 100644 index 000000000..be9a33714 --- /dev/null +++ b/x/evm/precompile/funtoken_test.go @@ -0,0 +1,712 @@ +package precompile_test + +import ( + "fmt" + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +// TestSuite: Runs all the tests in the suite. +type FuntokenSuite struct { + suite.Suite +} + +func TestFuntokenSuite(t *testing.T) { + suite.Run(t, new(FuntokenSuite)) +} + +func TestFailToPackABI(t *testing.T) { + testcases := []struct { + name string + methodName string + callArgs []any + wantError string + }{ + { + name: "wrong amount of call args", + methodName: string(precompile.FunTokenMethod_sendToBank), + callArgs: []any{"nonsense", "args here", "to see if", "precompile is", "called"}, + wantError: "argument count mismatch: got 5 for 3", + }, + { + name: "wrong type for address", + methodName: string(precompile.FunTokenMethod_sendToBank), + callArgs: []any{"nonsense", "foo", "bar"}, + wantError: "abi: cannot use string as type array as argument", + }, + { + name: "wrong type for amount", + methodName: string(precompile.FunTokenMethod_sendToBank), + callArgs: []any{gethcommon.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), "foo", testutil.AccAddress().String()}, + wantError: "abi: cannot use string as type ptr as argument", + }, + { + name: "wrong type for recipient", + methodName: string(precompile.FunTokenMethod_sendToBank), + callArgs: []any{gethcommon.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), big.NewInt(1), 111}, + wantError: "abi: cannot use int as type string as argument", + }, + { + name: "invalid method name", + methodName: "foo", + callArgs: []any{gethcommon.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6"), big.NewInt(1), testutil.AccAddress().String()}, + wantError: "method 'foo' not found", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + input, err := embeds.SmartContract_FunToken.ABI.Pack(tc.methodName, tc.callArgs...) + require.ErrorContains(t, err, tc.wantError) + require.Nil(t, input) + }) + } +} + +func TestWhoAmI(t *testing.T) { + deps := evmtest.NewTestDeps() + + callWhoAmI := func(arg string) (evmResp *evm.MsgEthereumTxResponse, err error) { + fmt.Printf("arg: %s", arg) + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack("whoAmI", arg) + require.NoError(t, err) + evmObj, _ := deps.NewEVM() + return deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_FunToken, + false, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + } + + for accIdx, acc := range []evmtest.EthPrivKeyAcc{ + deps.Sender, evmtest.NewEthPrivAcc(), + } { + t.Logf("test account %d, use both address formats", accIdx) + + for _, arg := range []string{acc.NibiruAddr.String(), acc.EthAddr.Hex()} { + evmResp, err := callWhoAmI(arg) + require.NoError(t, err) + gotAddrEth, gotAddrBech32, err := new(FunTokenWhoAmIReturn).ParseFromResp(evmResp) + require.NoError(t, err) + require.Equal(t, acc.EthAddr.Hex(), gotAddrEth.Hex()) + require.Equal(t, acc.NibiruAddr.String(), gotAddrBech32) + } + // Sad path check + _, err := callWhoAmI("not_an_address") + require.ErrorContains(t, err, "could not parse address as Nibiru Bech32 or Ethereum hexadecimal") + } +} + +func (s *FuntokenSuite) TestHappyPath() { + deps := evmtest.NewTestDeps() + + s.T().Log("Create FunToken mapping and ERC20") + funtoken := evmtest.CreateFunTokenForBankCoin(deps, evm.EVMBankDenom, &s.Suite) + erc20 := funtoken.Erc20Addr.Address + + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420))), + )) + + s.Run("IFunToken.bankBalance()", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack("bankBalance", deps.Sender.EthAddr, funtoken.BankDenom) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_FunToken, + false, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err, evmResp) + + bal, ethAddr, bech32Addr, err := new(FunTokenBankBalanceReturn).ParseFromResp(evmResp) + s.NoError(err) + s.Require().Zero(bal.Cmp(big.NewInt(69_420))) + s.Equal(deps.Sender.EthAddr.Hex(), ethAddr.Hex()) + s.Equal(deps.Sender.NibiruAddr.String(), bech32Addr) + }) + + s.Run("ConvertCoinToEvm", func() { + _, err := deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420)), + ToEthAddr: eth.EIP55Addr{ + Address: deps.Sender.EthAddr, + }, + }, + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + evmtest.AssertERC20BalanceEqualWithDescription( + s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(69_420), "expect 69420 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, evm.EVMBankDenom, deps.Sender.EthAddr, big.NewInt(0), "expect the sender to have zero balance") + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, evm.EVMBankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(69_420), "expect x/evm module to escrow all tokens") + }) + + s.Run("Mint tokens - Fail from non-owner", func() { + contractInput, err := embeds.SmartContract_ERC20Minter.ABI.Pack("mint", deps.Sender.EthAddr, big.NewInt(69_420)) + evmObj, _ := deps.NewEVM() + s.Require().NoError(err) + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &erc20, + false, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.ErrorContains(err, "Ownable: caller is not the owner") + }) + + s.Run("IFunToken.sendToBank()", func() { + randomAcc := testutil.AccAddress() + + input, err := embeds.SmartContract_FunToken.ABI.Pack(string(precompile.FunTokenMethod_sendToBank), erc20, big.NewInt(420), randomAcc.String()) + s.NoError(err) + + evmObj, _ := deps.NewEVM() + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_FunToken, + true, /*commit*/ + input, + keeper.Erc20GasLimitExecute, + ) + s.Require().NoError(err) + s.Require().Empty(ethTxResp.VmError) + + evmtest.AssertERC20BalanceEqualWithDescription( + s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(69_000), "expect 69000 balance remaining", + ) + evmtest.AssertERC20BalanceEqualWithDescription( + s.T(), deps, evmObj, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect 0 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, evm.EVMBankDenom, eth.NibiruAddrToEthAddr(randomAcc), big.NewInt(420), "expect 420 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, evm.EVMBankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(69_000), "expect 69000 balance", + ) + + s.T().Log("Parse the response contract addr and response bytes") + var sentAmt *big.Int + s.NoError(embeds.SmartContract_FunToken.ABI.UnpackIntoInterface( + &sentAmt, + string(precompile.FunTokenMethod_sendToBank), + ethTxResp.Ret, + )) + s.Require().Zero(sentAmt.Cmp(big.NewInt(420))) + }) + + s.Run("IFuntoken.balance", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack(string(precompile.FunTokenMethod_balance), deps.Sender.EthAddr, erc20) + s.Require().NoError(err) + + evmObj, _ := deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, // from + &precompile.PrecompileAddr_FunToken, // to + false, // commit + contractInput, + keeper.Erc20GasLimitQuery, + ) + s.Require().NoError(err, evmResp) + + bals, err := new(FunTokenBalanceReturn).ParseFromResp(evmResp) + s.NoError(err) + s.Equal(funtoken.Erc20Addr, bals.FunToken.Erc20Addr) + s.Equal(funtoken.BankDenom, bals.FunToken.BankDenom) + s.Equal(deps.Sender.EthAddr, bals.Account) + s.Zero(bals.BalanceBank.Cmp(big.NewInt(0))) + s.Zero(bals.BalanceERC20.Cmp(big.NewInt(69_000))) + }) +} + +func (s *FuntokenSuite) TestPrecompileLocalGas() { + deps := evmtest.NewTestDeps() + funtoken := evmtest.CreateFunTokenForBankCoin(deps, evm.EVMBankDenom, &s.Suite) + randomAcc := testutil.AccAddress() + + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestFunTokenPrecompileLocalGas, + funtoken.Erc20Addr.Address, + ) + s.Require().NoError(err) + contractAddr := deployResp.ContractAddr + + s.Run("Fund sender's wallet", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(funtoken.BankDenom, sdk.NewInt(1000))), + )) + }) + + s.Run("Fund contract with erc20 coins", func() { + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(funtoken.BankDenom, sdk.NewInt(1000)), + ToEthAddr: eth.EIP55Addr{ + Address: contractAddr, + }, + }, + ) + s.Require().NoError(err) + }) + + s.Run("Happy: callBankSend with default gas", func() { + contractInput, err := embeds.SmartContract_TestFunTokenPrecompileLocalGas.ABI.Pack( + "callBankSend", + big.NewInt(1), + randomAcc.String(), + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + resp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &contractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().NotZero(resp.GasUsed) + }) + + s.Run("Happy: callBankSend with local gas - sufficient gas amount", func() { + contractInput, err := embeds.SmartContract_TestFunTokenPrecompileLocalGas.ABI.Pack( + "callBankSendLocalGas", + big.NewInt(1), + randomAcc.String(), + big.NewInt(int64(evmtest.FunTokenGasLimitSendToEvm)), + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + resp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &contractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, // gasLimit for the entire call + ) + s.Require().NoError(err) + s.Require().NotZero(resp.GasUsed) + }) + + s.Run("Sad: callBankSend with local gas - insufficient gas amount", func() { + contractInput, err := embeds.SmartContract_TestFunTokenPrecompileLocalGas.ABI.Pack( + "callBankSendLocalGas", + big.NewInt(1), + randomAcc.String(), + big.NewInt(50_000), // customGas - too small + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + resp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &contractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, // gasLimit for the entire call + ) + s.Require().ErrorContains(err, "execution reverted") + s.Require().NotZero(resp.GasUsed) + }) +} + +func (s *FuntokenSuite) TestSendToEvm_MadeFromCoin() { + deps := evmtest.NewTestDeps() + + s.T().Log("create evmObj") + evmObj, _ := deps.NewEVM() + + s.T().Log("1) Create a new FunToken from coin 'ulibi'") + bankDenom := "ulibi" + funtoken := evmtest.CreateFunTokenForBankCoin(deps, bankDenom, &s.Suite) + erc20Addr := funtoken.Erc20Addr.Address + + s.T().Log("2) Fund the sender with some ulibi on the bank side") + err := testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(bankDenom, sdk.NewInt(1234))), + ) + s.Require().NoError(err) + + s.Run("Call sendToEvm(string bankDenom, uint256 amount, string to)", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( + string(precompile.FunTokenMethod_sendToEvm), + bankDenom, + big.NewInt(1000), + deps.Sender.EthAddr.Hex(), + ) + s.Require().NoError(err) + + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_FunToken, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().Empty(ethTxResp.VmError, "sendToEvm VMError") + + s.T().Log("The response returns the actual minted/unescrowed amount") + var amountSent *big.Int + err = embeds.SmartContract_FunToken.ABI.UnpackIntoInterface( + &amountSent, string(precompile.FunTokenMethod_sendToEvm), ethTxResp.Ret, + ) + s.Require().NoError(err) + s.Require().EqualValues(1000, amountSent.Int64(), "expect 1000 minted to EVM") + + s.T().Log("Check the user lost 1000 ulibi in bank") + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, bankDenom, deps.Sender.EthAddr, big.NewInt(234), "did user lose 1000 ulibi from bank?") + + s.T().Log("Check the module account has 1000 ulibi") + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, bankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(1000), "expect 1000 balance") + + s.T().Log("Check the user gained 1000 in ERC20 representation") + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, deps.Sender.EthAddr, big.NewInt(1000), "expect 1000 balance") + }) + + //----------------------------------------------------------------------- + // 5) Now send some tokens *back* to the bank via `sendToBank`. + //----------------------------------------------------------------------- + // We'll pick a brand new random account to receive them. + + s.Run("Sending 400 tokens back from EVM to Cosmos bank => recipient:", func() { + randomRecipient := testutil.AccAddress() + + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( + string(precompile.FunTokenMethod_sendToBank), + erc20Addr, + big.NewInt(400), + randomRecipient.String(), + ) + s.Require().NoError(err) + + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_FunToken, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().Empty(ethTxResp.VmError, "sendToBank VMError") + + s.T().Log("Parse the returned amount from `sendToBank`") + var actualSent *big.Int + err = embeds.SmartContract_FunToken.ABI.UnpackIntoInterface( + &actualSent, string(precompile.FunTokenMethod_sendToBank), + ethTxResp.Ret, + ) + s.Require().NoError(err) + s.Require().EqualValues(big.NewInt(400), actualSent, "expect 400 minted back to bank") + + s.T().Log("Check sender's EVM balance has decreased by 400") + // The sender started with 1000 after the first sendToEvm + evmtest.AssertERC20BalanceEqualWithDescription( + s.T(), + deps, + evmObj, + erc20Addr, + deps.Sender.EthAddr, + big.NewInt(600), // 1000 - 400 + "expect 600 balance", + ) + + s.T().Log("Check the bank side got 400 more") + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), + deps, + bankDenom, + eth.NibiruAddrToEthAddr(randomRecipient), + big.NewInt(400), + "did the recipient get 400?", + ) + + s.T().Log("Confirm module account doesn't keep them (burn or escrow) for bank-based tokens") + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), + deps, + bankDenom, + evm.EVM_MODULE_ADDRESS, + big.NewInt(600), + "module should now have 600 left escrowed", + ) + }) +} + +func bigTokens(n int64) *big.Int { + e18 := big.NewInt(1e18) // 1e18 + return new(big.Int).Mul(big.NewInt(n), e18) +} + +func (s *FuntokenSuite) TestSendToEvm_MadeFromERC20() { + // Create ERC20 token + + // EVM Transfer - Send 500 tokens to Bob (EVM) + + // sendToBank - Send 100 tokens from bob to alice's bank balance (EVM -> Cosmos) + // - escrow erc20 token + // - mint cosmos token + + // sendToEVM - Send 100 tokens from alice to bob's EVM address (Cosmos -> EVM) + // - burn cosmos token + // - unescrow erc20 token + + deps := evmtest.NewTestDeps() + + alice := evmtest.NewEthPrivAcc() + bob := evmtest.NewEthPrivAcc() + + // Fund user so they can create funtoken from an ERC20 + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + // Deploy an ERC20 with 18 decimals + erc20Resp, err := evmtest.DeployContract(&deps, embeds.SmartContract_TestERC20) + s.Require().NoError(err, "failed to deploy test ERC20") + erc20Addr := erc20Resp.ContractAddr + + // create fun token from that erc20 + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + Sender: deps.Sender.NibiruAddr.String(), + FromErc20: ð.EIP55Addr{Address: erc20Addr}, + }, + ) + s.Require().NoError(err) + + // Transfer 500 tokens to bob => 500 * 10^18 raw + s.Run("Transfer 500 tokens to bob", func() { + contractInput, err := embeds.SmartContract_TestERC20.ABI.Pack( + "transfer", + bob.EthAddr, + bigTokens(500), // 500 in human sense + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &erc20Addr, + true, + contractInput, + keeper.Erc20GasLimitExecute, + ) + s.Require().NoError(err) + + // Now user should have 500 tokens => raw is 500 * 10^18 + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(500), "expect nonzero balance") + }) + + // sendToBank: e.g. 100 tokens => 100 * 1e18 raw + // expects to escrow on EVM side and mint on cosmos side + s.Run("send 100 tokens to alice", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( + string(precompile.FunTokenMethod_sendToBank), + erc20Addr, // address + bigTokens(100), + alice.NibiruAddr.String(), + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + resp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + bob.EthAddr, /* from */ + &precompile.PrecompileAddr_FunToken, /* to */ + true, /* commit */ + contractInput, + evmtest.FunTokenGasLimitSendToEvm, /* gasLimit */ + ) + s.Require().NoError(err) + s.Require().Empty(resp.VmError) + + // Bank side should see 100 + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, "erc20/"+erc20Addr.Hex(), alice.EthAddr, bigTokens(100), "expect 100 balance") + + // Expect user to have 400 tokens => 400 * 10^18 + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(400), "expect Bob's balance to be 400") + + // 100 tokens are escrowed + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, evm.EVM_MODULE_ADDRESS, bigTokens(100), "expect EVM module to escrow 100 tokens") + }) + + // Finally sendToEvm(100) -> (expects to burn on cosmos side and unescrow in the EVM side) + s.Run("send 100 tokens back to Bob", func() { + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( + "sendToEvm", + "erc20/"+erc20Addr.Hex(), + bigTokens(100), + bob.EthAddr.Hex(), + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + resp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + alice.EthAddr, + &precompile.PrecompileAddr_FunToken, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + s.Require().Empty(resp.VmError) + + // no bank side left for alice + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, "erc20/"+erc20Addr.Hex(), alice.EthAddr, bigTokens(0), "expect 0 balance") + + // check bob has 500 tokens again => 500 * 1e18 + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, bob.EthAddr, bigTokens(500), "expect nonzero balance") + + // check evm module account's balance, it should have escrowed some tokens + // unescrow the tokens + evmtest.AssertERC20BalanceEqualWithDescription(s.T(), deps, evmObj, erc20Addr, evm.EVM_MODULE_ADDRESS, bigTokens(0), "expect zero balance") + + // burns the bank tokens + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, "erc20/"+erc20Addr.Hex(), evm.EVM_MODULE_ADDRESS, bigTokens(0), "expect 0 balance") + }) +} + +// FunTokenWhoAmIReturn holds the return values from the "IFuntoken.whoAmI" +// method. The return bytes from successful calls of that method can be ABI +// unpacked into this struct. +type FunTokenWhoAmIReturn struct { + NibiruAcc struct { + EthAddr gethcommon.Address `abi:"ethAddr"` + Bech32Addr string `abi:"bech32Addr"` + } `abi:"whoAddrs"` +} + +func (out FunTokenWhoAmIReturn) ParseFromResp( + evmResp *evm.MsgEthereumTxResponse, +) (ethAddr gethcommon.Address, bech32Addr string, err error) { + err = embeds.SmartContract_FunToken.ABI.UnpackIntoInterface( + &out, + "whoAmI", + evmResp.Ret, + ) + if err != nil { + return + } + return out.NibiruAcc.EthAddr, out.NibiruAcc.Bech32Addr, nil +} + +// FunTokenBalanceReturn holds the return values from the "IFuntoken.balance" +// method. The return bytes from successful calls of that method can be ABI +// unpacked into this struct. +type FunTokenBalanceReturn struct { + Erc20Bal *big.Int `abi:"erc20Balance"` + BankBal *big.Int `abi:"bankBalance"` + Token struct { + Erc20 gethcommon.Address `abi:"erc20"` + BankDenom string `abi:"bankDenom"` + } `abi:"token"` + NibiruAcc struct { + EthAddr gethcommon.Address `abi:"ethAddr"` + Bech32Addr string `abi:"bech32Addr"` + } `abi:"whoAddrs"` +} + +func (out FunTokenBalanceReturn) ParseFromResp( + evmResp *evm.MsgEthereumTxResponse, +) (bals evmtest.FunTokenBalanceAssert, err error) { + err = embeds.SmartContract_FunToken.ABI.UnpackIntoInterface( + &out, + "balance", + evmResp.Ret, + ) + if err != nil { + return + } + return evmtest.FunTokenBalanceAssert{ + FunToken: evm.FunToken{ + Erc20Addr: eth.EIP55Addr{Address: out.Token.Erc20}, + BankDenom: out.Token.BankDenom, + }, + Account: out.NibiruAcc.EthAddr, + BalanceBank: out.BankBal, + BalanceERC20: out.Erc20Bal, + }, nil +} + +// FunTokenBankBalanceReturn holds the return values from the +// "IFuntoken.bankBalance" method. The return bytes from successful calls of that +// method can be ABI unpacked into this struct. +type FunTokenBankBalanceReturn struct { + BankBal *big.Int `abi:"bankBalance"` + NibiruAcc struct { + EthAddr gethcommon.Address `abi:"ethAddr"` + Bech32Addr string `abi:"bech32Addr"` + } `abi:"whoAddrs"` +} + +func (out FunTokenBankBalanceReturn) ParseFromResp( + evmResp *evm.MsgEthereumTxResponse, +) (bal *big.Int, ethAddr gethcommon.Address, bech32Addr string, err error) { + err = embeds.SmartContract_FunToken.ABI.UnpackIntoInterface( + &out, + "bankBalance", + evmResp.Ret, + ) + if err != nil { + return + } + return out.BankBal, out.NibiruAcc.EthAddr, out.NibiruAcc.Bech32Addr, nil +} diff --git a/x/evm/precompile/nibiru_evm_utils.go b/x/evm/precompile/nibiru_evm_utils.go new file mode 100644 index 000000000..08805481f --- /dev/null +++ b/x/evm/precompile/nibiru_evm_utils.go @@ -0,0 +1,108 @@ +package precompile + +import ( + "bytes" + "fmt" + + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +// EvmEventAbciEvent is the string key used to retrieve the "AbciEvent" Ethereum +// ABI event for a precompiled contract that implements the "INibiruEvm" +// interface from "Nibiru/x/evm/embeds/contracts/NibiruEvmUtils.sol". +const EvmEventAbciEvent = "AbciEvent" + +// EmitEventAbciEvents adds a sequence of ABCI events to the EVM state DB so that +// they can be emitted at the end of the "EthereumTx". These events are indexed +// by their ABCI event type and help communicate non-EVM events in Ethereum-based +// block explorers and indexers by saving the event attributes in JSON form. +// +// Instead of ABI packing the non-indexed argument, this function encodes the +// [gethcore.Log].Data as a JSON string directly to optimize readbility in +// explorers without requiring the reader to decode using an ABI. +// +// Simply use ["encoding/hex".DecodeString] with the "0x" prefix removed to read +// the ABCI event. +func EmitEventAbciEvents( + ctx sdk.Context, + db *statedb.StateDB, + abciEvents []sdk.Event, + emittingAddr gethcommon.Address, +) { + blockNumber := uint64(ctx.BlockHeight()) + event := embeds.SmartContract_Wasm.ABI.Events[EvmEventAbciEvent] + for _, abciEvent := range abciEvents { + // Why 2 topics? Because 2 = event ID + number of indexed event fields + topics := make([]gethcommon.Hash, 2) + topics[0] = event.ID + + // eventType is the first (and only) indexed field + topics[1] = EventTopicFromString(abciEvent.Type) + + attrsBz := AttrsToJSON(append([]abci.EventAttribute{ + {Key: "eventType", Value: abciEvent.Type}, + }, abciEvent.Attributes...)) + nonIndexedArgs, _ := event.Inputs.NonIndexed().Pack(string(attrsBz)) + db.AddLog(&gethcore.Log{ + Address: emittingAddr, + Topics: topics, + Data: nonIndexedArgs, + BlockNumber: blockNumber, + }) + } +} + +// AttrsToJSON creates a deterministic JSON encoding for the key-value tuples. +func AttrsToJSON(attrs []abci.EventAttribute) []byte { + if len(attrs) == 0 { + return []byte("") + } + keysSeen := set.New[string]() + + // Create JSON object from the key-value tuples + var buf bytes.Buffer + buf.WriteByte('{') + for i, attr := range attrs { + // Keys must be unique to guarantee valid JSON object + if keysSeen.Has(attr.Key) { + continue + } + keysSeen.Add(attr.Key) + + if i > 0 { + buf.WriteByte(',') + } + + // Quote key and value + _, _ = fmt.Fprintf(&buf, `"%s":"%s"`, attr.Key, attr.Value) + } + buf.WriteByte('}') + + return buf.Bytes() +} + +// EventTopicFromBytes creates a "Topic" hash for an EVM event log. +// An event topic is a 32-byte field used to index specific fields in a smart +// contract event. Topics make it possible to efficiently filter for and search +// events in transaction logs. +func EventTopicFromBytes(bz []byte) (topic gethcommon.Hash) { + hash := crypto.Keccak256Hash(bz) + copy(topic[:], hash[:]) + return topic +} + +// EventTopicFromString creates a "Topic" hash for an EVM event log. +// An event topic is a 32-byte field used to index specific fields in a smart +// contract event. Topics make it possible to efficiently filter for and search +// events in transaction logs. +func EventTopicFromString(str string) (topic gethcommon.Hash) { + return EventTopicFromBytes([]byte(str)) +} diff --git a/x/evm/precompile/nibiru_evm_utils_test.go b/x/evm/precompile/nibiru_evm_utils_test.go new file mode 100644 index 000000000..d4121f81b --- /dev/null +++ b/x/evm/precompile/nibiru_evm_utils_test.go @@ -0,0 +1,174 @@ +package precompile_test + +import ( + "encoding/json" + "fmt" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + abibind "github.com/ethereum/go-ethereum/accounts/abi/bind" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +type UtilsSuite struct { + suite.Suite +} + +func TestUtilsSuite(t *testing.T) { + suite.Run(t, new(UtilsSuite)) +} + +func (s *UtilsSuite) TestAttrsToJSON() { + testCases := []struct { + name string + attrs []abci.EventAttribute + want string + }{ + { + name: "repeated key - last value wins", + attrs: []abci.EventAttribute{ + {Key: "action", Value: "first"}, + {Key: "action", Value: "second"}, + {Key: "amount", Value: "100"}, + }, + want: `{"action":"first","amount":"100"}`, + }, + { + name: "three unique attributes", + attrs: []abci.EventAttribute{ + {Key: "sender", Value: "addr1"}, + {Key: "recipient", Value: "addr2"}, + {Key: "amount", Value: "150"}, + }, + want: `{"sender":"addr1","recipient":"addr2","amount":"150"}`, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + got := string(precompile.AttrsToJSON(tc.attrs)) + s.Equal(tc.want, got) + }) + } +} + +func (s *UtilsSuite) TestEmitEventAbciEvent() { + abiEventName := precompile.EvmEventAbciEvent + abi := embeds.SmartContract_FunToken.ABI + event, some := abi.Events[abiEventName] + s.True(some, abiEventName) + eventId := event.ID + + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + + s.T().Log("Mint coins to generate ABCI events") + startIdx := len(deps.Ctx.EventManager().Events()) + dbStartIdx := len(db.Logs()) + err := deps.App.BankKeeper.MintCoins(deps.Ctx, evm.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 420_000)), + ) + s.NoError(err) + + abciEvents := deps.Ctx.EventManager().Events()[startIdx:] + s.Lenf(abciEvents, 2, "%+s", abciEvents) + + emittingAddr := precompile.PrecompileAddr_Wasm + precompile.EmitEventAbciEvents(deps.Ctx, db, abciEvents, emittingAddr) + blockNumber := uint64(deps.Ctx.BlockHeight()) + evmAddrBech32 := eth.EthAddrToNibiruAddr(evm.EVM_MODULE_ADDRESS) + type Want struct { + EventLog gethcore.Log + } + wants := []Want{ + { + EventLog: gethcore.Log{ + Address: emittingAddr, + Topics: []gethcommon.Hash{ + eventId, + precompile.EventTopicFromString(`coin_received`), + }, + Data: []byte(fmt.Sprintf( + `{"eventType":"coin_received","receiver":"%s","amount":"420000unibi"}`, evmAddrBech32), + ), + BlockNumber: blockNumber, + Index: uint(dbStartIdx), + }, + }, + { + EventLog: gethcore.Log{ + Address: emittingAddr, + Topics: []gethcommon.Hash{ + eventId, + precompile.EventTopicFromString(`coinbase`), + }, + Data: []byte(fmt.Sprintf( + `{"eventType":"coinbase","minter":"%s","amount":"420000unibi"}`, evmAddrBech32), + ), + BlockNumber: blockNumber, + Index: uint(dbStartIdx + 1), + }, + }, + } + + s.T().Log("Define the ABI and smart contract that will unpack the event data") + + boundContract := abibind.NewBoundContract( + emittingAddr, + *abi, + // verbose but descriptive to write out the interface implementations that are unused + (abibind.ContractCaller)(nil), + (abibind.ContractTransactor)(nil), + (abibind.ContractFilterer)(nil), + ) + + debugBz, _ := json.MarshalIndent(abciEvents, "", " ") + dbLogs := db.Logs() + for idx, want := range wants { + gotEventLog := *dbLogs[dbStartIdx+idx] + + s.T().Log("Check event log fields") + // logDataHex: Geth stores the bytes as a hex string (hexutil.Bytes) + logDataHex := hexutil.Bytes(gotEventLog.Data).String() + logDataHexDecoded, err := hexutil.Decode(logDataHex) + s.NoErrorf(err, "logDataHex: %s") + s.Contains(string(logDataHexDecoded), string(want.EventLog.Data)) + { + w, g := want.EventLog.Topics, gotEventLog.Topics + s.Require().EqualValuesf(w, g, "events:\n%#s", debugBz) + } + { + w, g := want.EventLog.Address.Hex(), gotEventLog.Address.Hex() + s.Require().EqualValuesf(w, g, "events:\n%#s", debugBz) + } + { + w, g := string(want.EventLog.Data), string(gotEventLog.Data) + s.Require().Containsf(g, w, "events:\n%#s", debugBz) + } + + s.T().Log("Use geth/.../abi/bind Go bindings to test ABI event decoding") + eventMap := make(map[string]any) + err = boundContract.UnpackLogIntoMap(eventMap, abiEventName, gotEventLog) + s.Require().NoError(err) + + abciEventValUntyped, isSome := eventMap["abciEvent"] + s.Truef(isSome, "%+s", eventMap) + abciEventVal, ok := abciEventValUntyped.(string) + s.True(ok, "%+s\nttype of abciEventVal: %T", eventMap, abciEventValUntyped) + s.Equal(string(want.EventLog.Data), string(abciEventVal), "%+s", eventMap) + + _, isSome = eventMap["eventType"] + s.Truef(isSome, "%+s", eventMap) + } +} diff --git a/x/evm/precompile/oracle.go b/x/evm/precompile/oracle.go new file mode 100644 index 000000000..12f559515 --- /dev/null +++ b/x/evm/precompile/oracle.go @@ -0,0 +1,187 @@ +package precompile + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/app/keepers" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + oraclekeeper "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" +) + +var _ vm.PrecompiledContract = (*precompileOracle)(nil) + +// Precompile address for "Oracle.sol", the contract that enables queries for exchange rates +var PrecompileAddr_Oracle = gethcommon.HexToAddress("0x0000000000000000000000000000000000000801") + +func (p precompileOracle) Address() gethcommon.Address { + return PrecompileAddr_Oracle +} + +func (p precompileOracle) RequiredGas(input []byte) (gasPrice uint64) { + return requiredGas(input, p.ABI()) +} + +func (p precompileOracle) ABI() *gethabi.ABI { + return embeds.SmartContract_Oracle.ABI +} + +const ( + OracleMethod_queryExchangeRate PrecompileMethod = "queryExchangeRate" + OracleMethod_chainLinkLatestRoundData PrecompileMethod = "chainLinkLatestRoundData" +) + +// Run runs the precompiled contract +func (p precompileOracle) Run( + evm *vm.EVM, contract *vm.Contract, readonly bool, +) (bz []byte, err error) { + defer func() { + err = ErrPrecompileRun(err, p) + }() + startResult, err := OnRunStart(evm, contract.Input, p.ABI(), contract.Gas) + if err != nil { + return nil, err + } + method, args, ctx := startResult.Method, startResult.Args, startResult.CacheCtx + + switch PrecompileMethod(method.Name) { + case OracleMethod_queryExchangeRate: + bz, err = p.queryExchangeRate(ctx, method, args) + // For "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol" + case OracleMethod_chainLinkLatestRoundData: + bz, err = p.chainLinkLatestRoundData(ctx, method, args) + + default: + // Note that this code path should be impossible to reach since + // "[decomposeInput]" parses methods directly from the ABI. + err = fmt.Errorf("invalid method called with name \"%s\"", method.Name) + return + } + contract.UseGas(startResult.CacheCtx.GasMeter().GasConsumed()) + if err != nil { + return nil, err + } + + return bz, err +} + +func PrecompileOracle(keepers keepers.PublicKeepers) vm.PrecompiledContract { + return precompileOracle{ + oracleKeeper: keepers.OracleKeeper, + } +} + +type precompileOracle struct { + oracleKeeper oraclekeeper.Keeper +} + +// Implements "IOracle.queryExchangeRate" +// +// ```solidity +// function queryExchangeRate( +// string memory pair +// ) +// external +// view +// returns (uint256 price, uint64 blockTimeMs, uint64 blockHeight); +// ``` +func (p precompileOracle) queryExchangeRate( + ctx sdk.Context, + method *gethabi.Method, + args []any, +) (bz []byte, err error) { + pair, err := p.parseQueryExchangeRateArgs(args) + if err != nil { + return nil, err + } + assetPair, err := asset.TryNewPair(pair) + if err != nil { + return nil, err + } + + priceAtBlock, err := p.oracleKeeper.ExchangeRates.Get(ctx, assetPair) + if err != nil { + return nil, err + } + + return method.Outputs.Pack( + priceAtBlock.ExchangeRate.BigInt(), + uint64(priceAtBlock.BlockTimestampMs), + priceAtBlock.CreatedBlock, + ) +} + +func (p precompileOracle) parseQueryExchangeRateArgs(args []any) ( + pair string, + err error, +) { + if e := assertNumArgs(args, 1); e != nil { + err = e + return + } + + pair, ok := args[0].(string) + if !ok { + err = ErrArgTypeValidation("string pair", args[0]) + return + } + + return pair, nil +} + +// Implements "IOracle.chainLinkLatestRoundData" +// +// ```solidity +// interface IOracle { +// function chainLinkLatestRoundData( +// string memory pair +// ) +// external +// view +// returns ( +// uint80 roundId, +// int256 answer, +// uint256 startedAt, +// uint256 updatedAt, +// uint80 answeredInRound +// ); +// // ... +// } +// ``` +func (p precompileOracle) chainLinkLatestRoundData( + ctx sdk.Context, + method *gethabi.Method, + args []any, +) (bz []byte, err error) { + pair, err := p.parseQueryExchangeRateArgs(args) + if err != nil { + return nil, err + } + assetPair, err := asset.TryNewPair(pair) + if err != nil { + return nil, err + } + + priceAtBlock, err := p.oracleKeeper.ExchangeRates.Get(ctx, assetPair) + if err != nil { + return nil, err + } + + roundId := new(big.Int).SetUint64(priceAtBlock.CreatedBlock) + answer := priceAtBlock.ExchangeRate.BigInt() // 18 decimals + timestampSeconds := big.NewInt(priceAtBlock.BlockTimestampMs / 1000) + answeredInRound := big.NewInt(420) // for no reason in particular / unused + return method.Outputs.Pack( + roundId, + answer, + timestampSeconds, // startedAt (seconds) + timestampSeconds, // updatedAt (seconds) + answeredInRound, + ) +} diff --git a/x/evm/precompile/oracle_test.go b/x/evm/precompile/oracle_test.go new file mode 100644 index 000000000..affb7b666 --- /dev/null +++ b/x/evm/precompile/oracle_test.go @@ -0,0 +1,172 @@ +package precompile_test + +import ( + "fmt" + "math/big" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +const OracleGasLimitQuery = 100_000 + +func (s *OracleSuite) TestOracle_FailToPackABI() { + testcases := []struct { + name string + methodName string + callArgs []any + wantError string + }{ + { + name: "wrong amount of call args", + methodName: string(precompile.OracleMethod_queryExchangeRate), + callArgs: []any{"nonsense", "args here", "to see if", "precompile is", "called"}, + wantError: "argument count mismatch: got 5 for 1", + }, + { + name: "wrong type for pair", + methodName: string(precompile.OracleMethod_queryExchangeRate), + callArgs: []any{common.HexToAddress("0x7D4B7B8CA7E1a24928Bb96D59249c7a5bd1DfBe6")}, + wantError: "abi: cannot use array as type string as argument", + }, + { + name: "invalid method name", + methodName: "foo", + callArgs: []any{"ubtc:uusdc"}, + wantError: "method 'foo' not found", + }, + } + + abi := embeds.SmartContract_Oracle.ABI + + for _, tc := range testcases { + s.Run(tc.name, func() { + input, err := abi.Pack(tc.methodName, tc.callArgs...) + s.ErrorContains(err, tc.wantError) + s.Nil(input) + }) + } +} + +func (s *OracleSuite) TestOracle_HappyPath() { + deps := evmtest.NewTestDeps() + runQuery := func(ctx sdk.Context) ( + resp *evm.MsgEthereumTxResponse, + err error, + ) { + contractInput, err := embeds.SmartContract_Oracle.ABI.Pack( + string(precompile.OracleMethod_queryExchangeRate), + "unibi:uusd", + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + return deps.EvmKeeper.CallContractWithInput( + ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Oracle, + false, + contractInput, + OracleGasLimitQuery, + ) + } + + s.T().Log("Query exchange rate") + { + // 69 seconds + 420 nanoseconds === 69000 milliseconds for the + // return value from the UnixMilli() function + deps.Ctx = deps.Ctx.WithBlockTime(time.Unix(69, 420)).WithBlockHeight(69) + deps.App.OracleKeeper.SetPrice(deps.Ctx, "unibi:uusd", sdk.MustNewDecFromStr("0.067")) + + resp, err := runQuery(deps.Ctx) + s.NoError(err) + + // Check the response + out, err := embeds.SmartContract_Oracle.ABI.Unpack( + string(precompile.OracleMethod_queryExchangeRate), resp.Ret, + ) + s.NoError(err) + s.Equal(out[0].(*big.Int), big.NewInt(67_000_000_000_000_000)) + s.Equal(fmt.Sprintf("%d", out[1].(uint64)), "69000") + s.Equal(fmt.Sprintf("%d", out[2].(uint64)), "69") + } + + s.T().Log("Query from a later time") + { + secondsLater := deps.Ctx.BlockTime().Add(100 * time.Second) + resp, err := runQuery(deps.Ctx. + WithBlockTime(secondsLater). + WithBlockHeight(deps.Ctx.BlockHeight() + 50), + ) + s.NoError(err) + + // Check the response + out, err := embeds.SmartContract_Oracle.ABI.Unpack( + string(precompile.OracleMethod_queryExchangeRate), resp.Ret, + ) + s.NoError(err) + // These terms should still be equal because the latest exchange rate + // has not changed. + s.Equal(out[0].(*big.Int), big.NewInt(67_000_000_000_000_000)) + s.Equal(fmt.Sprintf("%d", out[1].(uint64)), "69000") + s.Equal(fmt.Sprintf("%d", out[2].(uint64)), "69") + } + + s.T().Log("test IOracle.chainLinkLatestRoundData") + { + secondsLater := deps.Ctx.BlockTime().Add(100 * time.Second) + ctx := deps.Ctx. + WithBlockTime(secondsLater). + WithBlockHeight(deps.Ctx.BlockHeight() + 50) + + contractInput, err := embeds.SmartContract_Oracle.ABI.Pack( + string(precompile.OracleMethod_chainLinkLatestRoundData), + "unibi:uusd", + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + resp, err := deps.EvmKeeper.CallContractWithInput( + ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Oracle, + false, + contractInput, + OracleGasLimitQuery, + ) + s.NoError(err) + + // Check the response + out, err := embeds.SmartContract_Oracle.ABI.Unpack( + string(precompile.OracleMethod_chainLinkLatestRoundData), resp.Ret, + ) + s.NoError(err) + // roundId : created at block height 69 + s.Equal(out[0].(*big.Int), big.NewInt(69)) + // answer : exchange rate with 18 decimals. + // In this case, 0.067 = 67 * 10^{15}. + s.Equal(out[1].(*big.Int), big.NewInt(67_000_000_000_000_000)) + // startedAt, updatedAt : created at block timestamp + s.Equal(out[2].(*big.Int), new(big.Int).SetInt64(deps.Ctx.BlockTime().Unix())) + s.Equal(out[3].(*big.Int), new(big.Int).SetInt64(deps.Ctx.BlockTime().Unix())) + // answeredInRound + s.Equal(out[4].(*big.Int), big.NewInt(420)) + } +} + +type OracleSuite struct { + suite.Suite +} + +// TestPrecompileSuite: Runs all the tests in the suite. +func TestOracleSuite(t *testing.T) { + suite.Run(t, new(OracleSuite)) +} diff --git a/x/evm/precompile/precompile.go b/x/evm/precompile/precompile.go new file mode 100644 index 000000000..b7df0e845 --- /dev/null +++ b/x/evm/precompile/precompile.go @@ -0,0 +1,247 @@ +// Package precompile implements custom precompiles for the Nibiru EVM. +// +// Precompiles are special, built-in contract interfaces that exist at +// predefined addresses and run custom logic outside of what is possible +// in standard Solidity contracts. This package extends the default Ethereum +// precompiles with Nibiru-specific functionality. +// +// Key components: +// - InitPrecompiles: Initializes and returns a map of precompiled contracts. +// - PrecompileFunToken: Implements the FunToken precompile for ERC20-to-bank transfers. +// +// The package also provides utility functions for working with precompiles, such +// as "ABIMethodByID" and "OnRunStart" for common precompile execution setup. +package precompile + +import ( + "bytes" + "fmt" + + "github.com/NibiruChain/collections" + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/app/keepers" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +// InitPrecompiles initializes and returns a map of precompiled contracts for the EVM. +// It combines default Ethereum precompiles with custom Nibiru precompiles. +// +// Parameters: +// - k: A keepers.PublicKeepers instance providing access to various blockchain state. +// +// Returns: +// - A map of Ethereum addresses to PrecompiledContract implementations. +func InitPrecompiles( + k keepers.PublicKeepers, +) (precompiles map[gethcommon.Address]vm.PrecompiledContract) { + precompiles = make(map[gethcommon.Address]vm.PrecompiledContract) + + // Default precompiles + for addr, pc := range vm.PrecompiledContractsBerlin { + precompiles[addr] = pc + } + + // Custom precompiles + for _, precompileSetupFn := range []func(k keepers.PublicKeepers) vm.PrecompiledContract{ + PrecompileFunToken, + PrecompileWasm, + PrecompileOracle, + } { + pc := precompileSetupFn(k) + precompiles[pc.Address()] = pc + } + + // TODO: feat(evm): implement precompiled contracts for ibc transfer + // Check if there is sufficient demand for this. + + // TODO: feat(evm): implement precompiled contracts for staking + // Note that liquid staked assets can be a useful alternative to adding a + // staking precompile. + // Check if there is sufficient demand for this. + + return precompiles +} + +// methodById: Looks up an ABI method by the 4-byte id. +// Copy of "ABI.MethodById" from go-ethereum version > 1.10 +func methodById(abi *gethabi.ABI, sigdata []byte) (*gethabi.Method, error) { + if len(sigdata) != 4 { + return nil, fmt.Errorf("data (%d bytes) insufficient for abi method lookup", len(sigdata)) + } + + for _, method := range abi.Methods { + if bytes.Equal(method.ID, sigdata[:4]) { + return &method, nil + } + } + + return nil, fmt.Errorf("no method with id: %#x", sigdata[:4]) +} + +func decomposeInput( + abi *gethabi.ABI, input []byte, +) (method *gethabi.Method, args []any, err error) { + // ABI method IDs are exactly 4 bytes according to "gethabi.ABI.MethodByID". + if len(input) < 4 { + err = fmt.Errorf("input \"%s\" too short to extract method ID (less than 4 bytes)", collections.HumanizeBytes(input)) + return + } + + method, err = methodById(abi, input[:4]) + if err != nil { + err = fmt.Errorf("unable to parse ABI method by its 4-byte ID: %w", err) + return + } + + args, err = method.Inputs.Unpack(input[4:]) + if err != nil { + err = fmt.Errorf("unable to unpack input args: %w", err) + return + } + + return method, args, nil +} + +func requiredGas(input []byte, abi *gethabi.ABI) uint64 { + method, err := methodById(abi, input[:4]) + if err != nil { + // It's appropriate to return a reasonable default here + // because the error from DecomposeInput will be handled automatically by + // "Run". In go-ethereum/core/vm/contracts.go, you can see the execution + // order of a precompile in the "runPrecompiledContract" function. + return gethparams.TxGas // return reasonable default + } + gasCfg := store.KVGasConfig() + + // Map access could panic. We know that it won't panic because all methods + // are in the map, which is verified by unit tests. + var costPerByte, costFlat uint64 + if isMutation[PrecompileMethod(method.Name)] { + costPerByte, costFlat = gasCfg.WriteCostPerByte, gasCfg.WriteCostFlat + } else { + costPerByte, costFlat = gasCfg.ReadCostPerByte, gasCfg.ReadCostFlat + } + + // Calculate the total gas required based on the input size and flat cost + return (costPerByte * uint64(len(input[4:]))) + costFlat +} + +type PrecompileMethod string + +type OnRunStartResult struct { + // Args contains the decoded (ABI unpacked) arguments passed to the contract + // as input. + Args []any + + // CacheCtx is a cached SDK context that allows isolated state + // operations to occur that can be reverted by the EVM's [statedb.StateDB]. + CacheCtx sdk.Context + + // Method is the ABI method for the precompiled contract call. + Method *gethabi.Method + + StateDB *statedb.StateDB + + PrecompileJournalEntry statedb.PrecompileCalled +} + +// OnRunStart prepares the execution environment for a precompiled contract call. +// It handles decoding the input data according the to contract ABI, creates an +// isolated cache context for state changes, and sets up a snapshot for potential +// EVM "reverts". +// +// Args: +// - evm: Instance of the EVM executing the contract +// - contract: Precompiled contract being called +// - abi: [gethabi.ABI] defining the contract's invokable methods. +// +// Example Usage: +// +// ```go +// func (p newPrecompile) Run( +// evm *vm.EVM, contract *vm.Contract, readonly bool +// ) (bz []byte, err error) { +// res, err := OnRunStart(evm, contract, p.ABI()) +// if err != nil { +// return nil, err +// } +// // ... +// // Use res.Ctx for state changes +// // Use res.StateDB.Commit() before any non-EVM state changes +// // to guarantee the context and [statedb.StateDB] are in sync. +// } +// ``` +func OnRunStart( + evm *vm.EVM, contractInput []byte, abi *gethabi.ABI, gasLimit uint64, +) (res OnRunStartResult, err error) { + method, args, err := decomposeInput(abi, contractInput) + if err != nil { + return res, err + } + + stateDB, ok := evm.StateDB.(*statedb.StateDB) + if !ok { + err = fmt.Errorf("failed to load the sdk.Context from the EVM StateDB") + return + } + + // journalEntry captures the state before precompile execution to enable + // proper state reversal if the call fails or if [statedb.JournalChange] + // is reverted in general. + cacheCtx, journalEntry := stateDB.CacheCtxForPrecompile() + if err = stateDB.SavePrecompileCalledJournalChange(journalEntry); err != nil { + return res, err + } + if err = stateDB.CommitCacheCtx(); err != nil { + return res, fmt.Errorf("error committing dirty journal entries: %w", err) + } + + // Switching to a local gas meter to enforce gas limit check for a precompile + cacheCtx = cacheCtx.WithGasMeter(sdk.NewGasMeter(gasLimit)). + WithKVGasConfig(store.KVGasConfig()). + WithTransientKVGasConfig(store.TransientGasConfig()) + + return OnRunStartResult{ + Args: args, + CacheCtx: cacheCtx, + Method: method, + StateDB: stateDB, + }, nil +} + +var isMutation map[PrecompileMethod]bool = map[PrecompileMethod]bool{ + WasmMethod_execute: true, + WasmMethod_instantiate: true, + WasmMethod_executeMulti: true, + WasmMethod_query: false, + WasmMethod_queryRaw: false, + + FunTokenMethod_sendToBank: true, + FunTokenMethod_balance: false, + FunTokenMethod_bankBalance: false, + FunTokenMethod_whoAmI: false, + + FunTokenMethod_sendToEvm: true, + FunTokenMethod_bankMsgSend: true, + + OracleMethod_queryExchangeRate: false, +} + +func HandleOutOfGasPanic(err *error) func() { + return func() { + if r := recover(); r != nil { + switch r.(type) { + case sdk.ErrorOutOfGas: + *err = vm.ErrOutOfGas + default: + panic(r) + } + } + } +} diff --git a/x/evm/precompile/test/bank_transfer.wasm b/x/evm/precompile/test/bank_transfer.wasm new file mode 100644 index 000000000..7f1fa8f6f Binary files /dev/null and b/x/evm/precompile/test/bank_transfer.wasm differ diff --git a/x/evm/precompile/test/counter.wasm b/x/evm/precompile/test/counter.wasm new file mode 100644 index 000000000..0e1cba209 Binary files /dev/null and b/x/evm/precompile/test/counter.wasm differ diff --git a/x/evm/precompile/test/export.go b/x/evm/precompile/test/export.go new file mode 100644 index 000000000..01104f0bf --- /dev/null +++ b/x/evm/precompile/test/export.go @@ -0,0 +1,307 @@ +package test + +import ( + "encoding/json" + "os" + "os/exec" + "path" + "strings" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasm "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +// rough gas limits for wasm execution - used in tests only +const ( + WasmGasLimitInstantiate uint64 = 1_000_000 + WasmGasLimitExecute uint64 = 10_000_000 + WasmGasLimitQuery uint64 = 200_000 +) + +// SetupWasmContracts stores all Wasm bytecode and has the "deps.Sender" +// instantiate each Wasm contract using the precompile. +func SetupWasmContracts(deps *evmtest.TestDeps, s *suite.Suite) ( + contracts []sdk.AccAddress, +) { + wasmCodes := deployWasmBytecode(s, deps.Ctx, deps.Sender.NibiruAddr, deps.App) + + instantiateArgs := []struct { + InstantiateMsg []byte + Label string + }{ + { + InstantiateMsg: []byte("{}"), + Label: "https://github.com/NibiruChain/nibiru-wasm/blob/main/contracts/nibi-stargate/src/contract.rs", + }, + { + InstantiateMsg: []byte(`{"count": 0}`), + Label: "https://github.com/NibiruChain/nibiru-wasm/tree/ec3ab9f09587a11fbdfbd4021c7617eca3912044/contracts/00-hello-world-counter", + }, + { + InstantiateMsg: []byte(`{}`), + Label: "https://github.com/k-yang/nibiru-wasm-plus/tree/main/contracts/bank-transfer/", + }, + { + InstantiateMsg: []byte(`{}`), + Label: "https://github.com/k-yang/nibiru-wasm-plus/tree/main/contracts/staking/", + }, + } + + for i, wasmCode := range wasmCodes { + s.T().Logf("Instantiate %s", wasmCode.binPath) + + wasmPermissionedKeeper := wasmkeeper.NewDefaultPermissionKeeper(deps.App.WasmKeeper) + contractAddr, _, err := wasmPermissionedKeeper.Instantiate( + deps.Ctx, + wasmCode.codeId, + deps.Sender.NibiruAddr, + deps.Sender.NibiruAddr, + instantiateArgs[i].InstantiateMsg, + instantiateArgs[i].Label, + sdk.Coins{}, + ) + s.NoError(err) + contracts = append(contracts, contractAddr) + } + + return contracts +} + +// deployWasmBytecode is a setup function that stores all Wasm bytecode used in +// the test suite. +func deployWasmBytecode( + s *suite.Suite, + ctx sdk.Context, + sender sdk.AccAddress, + app *app.NibiruApp, +) (codeIds []struct { + codeId uint64 + binPath string +}, +) { + // rootPath, _ := exec.Command("go list -m -f {{.Dir}}").Output() + // Run: go list -m -f {{.Dir}} + // This returns the path to the root of the project. + rootPathBz, err := exec.Command("go", "list", "-m", "-f", "{{.Dir}}").Output() + s.Require().NoError(err) + rootPath := strings.Trim(string(rootPathBz), "\n") + for _, wasmFile := range []string{ + // nibi_stargate.wasm is a compiled version of: + // https://github.com/NibiruChain/nibiru-wasm/blob/main/contracts/nibi-stargate/src/contract.rs + "x/tokenfactory/fixture/nibi_stargate.wasm", + + // hello_world_counter.wasm is a compiled version of: + // https://github.com/NibiruChain/nibiru-wasm/tree/ec3ab9f09587a11fbdfbd4021c7617eca3912044/contracts/00-hello-world-counter + "x/evm/precompile/test/hello_world_counter.wasm", + + // bank_transfer.wasm is a compiled version of: + // https://github.com/k-yang/nibiru-wasm-plus/tree/main/contracts/bank-transfer/ + "x/evm/precompile/test/bank_transfer.wasm", + + // staking.wasm is a compiled version of: + // https://github.com/k-yang/nibiru-wasm-plus/tree/main/contracts/staking/ + "x/evm/precompile/test/staking.wasm", + + // Add other wasm bytecode here if needed... + } { + binPath := path.Join(rootPath, wasmFile) + wasmBytecode, err := os.ReadFile(binPath) + s.Require().NoErrorf( + err, + "path %s, pathToWasmBin %s", binPath, + ) + + // The "Create" fn is private on the nibiru.WasmKeeper. By placing it as the + // decorated keeper in PermissionedKeeper type, we can access "Create" as a + // public fn. + wasmPermissionedKeeper := wasmkeeper.NewDefaultPermissionKeeper(app.WasmKeeper) + codeId, _, err := wasmPermissionedKeeper.Create( + ctx, sender, wasmBytecode, &wasm.AccessConfig{ + Permission: wasm.AccessTypeEverybody, + }, + ) + s.Require().NoError(err) + codeIds = append(codeIds, struct { + codeId uint64 + binPath string + }{codeId, binPath}) + } + + return codeIds +} + +// From IWasm.query of Wasm.sol: +// +// ```solidity +// function query( +// string memory contractAddr, +// bytes memory req +// ) external view returns (bytes memory response); +// ``` +func AssertWasmCounterState( + s *suite.Suite, + deps evmtest.TestDeps, + wasmContract sdk.AccAddress, + wantCount int64, +) { + msgArgsBz := []byte(` + { + "count": {} + } +`) + + resp, err := deps.App.WasmKeeper.QuerySmart(deps.Ctx, wasmContract, msgArgsBz) + s.Require().NoError(err) + s.Require().NotEmpty(resp) + + var typedResp QueryMsgCountResp + s.NoError(json.Unmarshal(resp, &typedResp)) + + s.EqualValues(wantCount, typedResp.Count) + s.EqualValues(deps.Sender.NibiruAddr.String(), typedResp.Owner) +} + +func AssertWasmCounterStateWithEvm( + s *suite.Suite, + deps evmtest.TestDeps, + evmObj *vm.EVM, + wasmContract sdk.AccAddress, + wantCount int64, +) { + msgArgsBz := []byte(` + { + "count": {} + } + `) + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_query), + wasmContract.String(), + msgArgsBz, + ) + s.Require().NoError(err) + + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + false, + contractInput, + WasmGasLimitQuery, + ) + s.Require().NoError(err) + s.Require().NotEmpty(evmResp.Ret) + + var queryResp []byte + s.NoError(embeds.SmartContract_Wasm.ABI.UnpackIntoInterface( + &queryResp, + string(precompile.WasmMethod_query), + evmResp.Ret, + )) + + var typedResp QueryMsgCountResp + s.NoError(json.Unmarshal(queryResp, &typedResp)) + + s.EqualValues(wantCount, typedResp.Count) + s.EqualValues(deps.Sender.NibiruAddr.String(), typedResp.Owner) +} + +// Result of QueryMsg::Count from the [hello_world_counter] Wasm contract: +// +// ```rust +// #[cw_serde] +// pub struct State { +// pub count: i64, +// pub owner: Addr, +// } +// ``` +// +// [hello_world_counter]: https://github.com/NibiruChain/nibiru-wasm/tree/ec3ab9f09587a11fbdfbd4021c7617eca3912044/contracts/00-hello-world-counter +type QueryMsgCountResp struct { + Count int64 `json:"count"` + Owner string `json:"owner"` +} + +// From evm/embeds/contracts/Wasm.sol: +// +// ```solidity +// struct WasmExecuteMsg { +// string contractAddr; +// bytes msgArgs; +// BankCoin[] funds; +// } +// +// /// @notice Identical to "execute", except for multiple contract calls. +// function executeMulti( +// WasmExecuteMsg[] memory executeMsgs +// ) payable external returns (bytes[] memory responses); +// ``` +// +// The increment call corresponds to the ExecuteMsg from +// the [hello_world_counter] Wasm contract: +// +// ```rust +// #[cw_serde] +// pub enum ExecuteMsg { +// Increment {}, // Increase count by 1 +// Reset { count: i64 }, // Reset to any i64 value +// } +// ``` +// +// [hello_world_counter]: https://github.com/NibiruChain/nibiru-wasm/tree/ec3ab9f09587a11fbdfbd4021c7617eca3912044/contracts/00-hello-world-counter +func IncrementWasmCounterWithExecuteMulti( + s *suite.Suite, + deps *evmtest.TestDeps, + evmObj *vm.EVM, + wasmContract sdk.AccAddress, + times uint, + commit bool, +) { + msgArgsBz := []byte(` + { + "increment": {} + } + `) + + // The "times" arg determines the number of messages in the executeMsgs slice + executeMsgs := []struct { + ContractAddr string `json:"contractAddr"` + MsgArgs []byte `json:"msgArgs"` + Funds []precompile.WasmBankCoin `json:"funds"` + }{} + + for i := uint(0); i < times; i++ { + executeMsgs = append(executeMsgs, struct { + ContractAddr string `json:"contractAddr"` + MsgArgs []byte `json:"msgArgs"` + Funds []precompile.WasmBankCoin `json:"funds"` + }{wasmContract.String(), msgArgsBz, []precompile.WasmBankCoin{}}) + } + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_executeMulti), + executeMsgs, + ) + s.Require().NoError(err) + + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + commit, + contractInput, + WasmGasLimitExecute, + ) + s.Require().NoError(err) + s.Require().NotEmpty(ethTxResp.Ret) +} diff --git a/x/evm/precompile/test/hello_world_counter.wasm b/x/evm/precompile/test/hello_world_counter.wasm new file mode 100644 index 000000000..9a532ca44 Binary files /dev/null and b/x/evm/precompile/test/hello_world_counter.wasm differ diff --git a/x/evm/precompile/test/infinite_loop.wasm b/x/evm/precompile/test/infinite_loop.wasm new file mode 100644 index 000000000..d58ca4100 Binary files /dev/null and b/x/evm/precompile/test/infinite_loop.wasm differ diff --git a/x/evm/precompile/test/staking.wasm b/x/evm/precompile/test/staking.wasm new file mode 100644 index 000000000..d902c74af Binary files /dev/null and b/x/evm/precompile/test/staking.wasm differ diff --git a/x/evm/precompile/test/wasteful_gas.wasm b/x/evm/precompile/test/wasteful_gas.wasm new file mode 100644 index 000000000..873a07113 Binary files /dev/null and b/x/evm/precompile/test/wasteful_gas.wasm differ diff --git a/x/evm/precompile/wasm.go b/x/evm/precompile/wasm.go new file mode 100644 index 000000000..bfb9ff9d8 --- /dev/null +++ b/x/evm/precompile/wasm.go @@ -0,0 +1,386 @@ +package precompile + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/NibiruChain/nibiru/v2/app/keepers" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + evmkeeper "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasm "github.com/CosmWasm/wasmd/x/wasm/types" + gethabi "github.com/ethereum/go-ethereum/accounts/abi" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" +) + +var _ vm.PrecompiledContract = (*precompileWasm)(nil) + +// Precompile address for "Wasm.sol", +var PrecompileAddr_Wasm = gethcommon.HexToAddress("0x0000000000000000000000000000000000000802") + +// Contract methods from Wasm.sol +const ( + WasmMethod_execute PrecompileMethod = "execute" + WasmMethod_query PrecompileMethod = "query" + WasmMethod_instantiate PrecompileMethod = "instantiate" + WasmMethod_executeMulti PrecompileMethod = "executeMulti" + WasmMethod_queryRaw PrecompileMethod = "queryRaw" +) + +// Run runs the precompiled contract +func (p precompileWasm) Run( + evm *vm.EVM, contract *vm.Contract, readonly bool, +) (bz []byte, err error) { + defer func() { + err = ErrPrecompileRun(err, p) + }() + startResult, err := OnRunStart(evm, contract.Input, p.ABI(), contract.Gas) + if err != nil { + return nil, err + } + + // Gracefully handles "out of gas" + defer HandleOutOfGasPanic(&err)() + + abciEventsStartIdx := len(startResult.CacheCtx.EventManager().Events()) + + switch PrecompileMethod(startResult.Method.Name) { + case WasmMethod_execute: + bz, err = p.execute(startResult, contract.CallerAddress, readonly) + case WasmMethod_query: + bz, err = p.query(startResult, contract) + case WasmMethod_instantiate: + bz, err = p.instantiate(startResult, contract.CallerAddress, readonly) + case WasmMethod_executeMulti: + bz, err = p.executeMulti(startResult, contract.CallerAddress, readonly) + case WasmMethod_queryRaw: + bz, err = p.queryRaw(startResult, contract) + default: + // Note that this code path should be impossible to reach since + // "[decomposeInput]" parses methods directly from the ABI. + err = fmt.Errorf("invalid method called with name \"%s\"", startResult.Method.Name) + return + } + // Gas consumed by a local gas meter + // The reason it's unnecessary to check for a success value is because + // GasConsumed is guaranteed to be less than the contract.Gas because the gas + // meter was initialized.... + contract.UseGas(startResult.CacheCtx.GasMeter().GasConsumed()) + + if err != nil { + return nil, err + } + + // Emit extra events for the EVM if this is a transaction + // https://github.com/NibiruChain/nibiru/issues/2121 + if isMutation[PrecompileMethod(startResult.Method.Name)] { + EmitEventAbciEvents( + startResult.CacheCtx, + startResult.StateDB, + startResult.CacheCtx.EventManager().Events()[abciEventsStartIdx:], + p.Address(), + ) + } + + return bz, err +} + +type precompileWasm struct { + *evmkeeper.Keeper + Wasm Wasm +} + +func (p precompileWasm) Address() gethcommon.Address { + return PrecompileAddr_Wasm +} + +// RequiredGas calculates the cost of calling the precompile in gas units. +func (p precompileWasm) RequiredGas(input []byte) (gasCost uint64) { + return requiredGas(input, p.ABI()) +} + +func (p precompileWasm) ABI() *gethabi.ABI { + return embeds.SmartContract_Wasm.ABI +} + +// Wasm: A struct embedding keepers for read and write operations in Wasm, such +// as execute, query, and instantiate. +type Wasm struct { + *wasmkeeper.PermissionedKeeper + wasmkeeper.Keeper +} + +func PrecompileWasm(keepers keepers.PublicKeepers) vm.PrecompiledContract { + return precompileWasm{ + Keeper: keepers.EvmKeeper, + Wasm: Wasm{ + wasmkeeper.NewDefaultPermissionKeeper(keepers.WasmKeeper), + keepers.WasmKeeper, + }, + } +} + +// execute invokes a Wasm contract's "ExecuteMsg", which corresponds to +// "wasm/types/MsgExecuteContract". This enables arbitrary smart contract +// execution using the Wasm VM from the EVM. +// +// Implements "execute" from evm/embeds/contracts/Wasm.sol: +// +// ```solidity +// function execute( +// string memory contractAddr, +// bytes memory msgArgs, +// BankCoin[] memory funds +// ) payable external returns (bytes memory response); +// ``` +// +// Contract Args: +// - contractAddr: nibi-prefixed Bech32 address of the wasm contract +// - msgArgs: JSON encoded wasm execute invocation +// - funds: Optional funds to supply during the execute call. It's +// uncommon to use this field, so you'll pass an empty array most of the time. +func (p precompileWasm) execute( + start OnRunStartResult, + caller gethcommon.Address, + readOnly bool, +) (bz []byte, err error) { + method, args, ctx := start.Method, start.Args, start.CacheCtx + defer func() { + if err != nil { + err = ErrMethodCalled(method, err) + } + }() + if err := assertNotReadonlyTx(readOnly, method); err != nil { + return nil, err + } + + wasmContract, msgArgsBz, funds, err := p.parseArgsWasmExecute(args) + if err != nil { + err = ErrInvalidArgs(err) + return + } + data, err := p.Wasm.Execute(ctx, wasmContract, eth.EthAddrToNibiruAddr(caller), msgArgsBz, funds) + if err != nil { + return + } + return method.Outputs.Pack(data) +} + +// query runs a smart query. In Rust, this is the "WasmQuery::Smart" variant. +// In protobuf/gRPC, it's type URL is +// "/cosmwasm.wasm.v1.Query/SmartContractState". +// +// Implements "query" from evm/embeds/contracts/Wasm.sol: +// +// ```solidity +// function query( +// string memory contractAddr, +// bytes memory req +// ) external view returns (bytes memory response); +// ``` +func (p precompileWasm) query( + start OnRunStartResult, + contract *vm.Contract, +) (bz []byte, err error) { + method, args, ctx := start.Method, start.Args, start.CacheCtx + defer func() { + if err != nil { + err = ErrMethodCalled(method, err) + } + }() + if err := assertContractQuery(contract); err != nil { + return bz, err + } + + wasmContract, req, err := p.parseArgsWasmQuery(args) + if err != nil { + err = ErrInvalidArgs(err) + return + } + respBz, err := p.Wasm.QuerySmart(ctx, wasmContract, req) + if err != nil { + return + } + return method.Outputs.Pack(respBz) +} + +// instantiate creates a new instance of a Wasm smart contract for some code id. +// +// Implements "instantiate" from evm/embeds/contracts/Wasm.sol: +// +// ```solidity +// /// @notice InstantiateContract creates a new smart contract instance for the given code id. +// /// @param admin The address of the contract admin (optional, can be empty string) +// /// @param codeID The ID of the code to instantiate +// /// @param msgArgs JSON encoded instantiation message +// /// @param label A human-readable label for the contract +// /// @param funds Optional funds to send to the contract upon instantiation +// function instantiate( +// string memory admin, +// uint64 codeID, +// bytes memory msgArgs, +// string memory label, +// BankCoin[] memory funds +// ) payable external returns (string memory contractAddr, bytes memory data); +// ``` +func (p precompileWasm) instantiate( + start OnRunStartResult, + caller gethcommon.Address, + readOnly bool, +) (bz []byte, err error) { + method, args, ctx := start.Method, start.Args, start.CacheCtx + defer func() { + if err != nil { + err = ErrMethodCalled(method, err) + } + }() + if err := assertNotReadonlyTx(readOnly, method); err != nil { + return nil, err + } + + callerBech32 := eth.EthAddrToNibiruAddr(caller) + txMsg, err := p.parseArgsWasmInstantiate(args, callerBech32.String()) + if err != nil { + err = ErrInvalidArgs(err) + return + } + + var adminAddr sdk.AccAddress + if len(txMsg.Admin) > 0 { + adminAddr = sdk.MustAccAddressFromBech32(txMsg.Admin) // validated in parse + } + contractAddr, data, err := p.Wasm.Instantiate( + ctx, txMsg.CodeID, callerBech32, adminAddr, txMsg.Msg, txMsg.Label, txMsg.Funds, + ) + if err != nil { + return + } + + return method.Outputs.Pack(contractAddr.String(), data) +} + +// executeMulti allows executing multiple Wasm contract calls in a single transaction. +// It corresponds to the "executeMulti" method in the IWasm interface. +// +// Implements "executeMulti" from evm/embeds/contracts/Wasm.sol: +// +// ```solidity +// /// @notice Identical to "execute", except for multiple contract calls. +// /// @param executeMsgs An array of WasmExecuteMsg structs, each containing: +// /// - contractAddr: nibi-prefixed Bech32 address of the wasm contract +// /// - msgArgs: JSON encoded wasm execute invocation +// /// - funds: Optional funds to supply during the execute call +// function executeMulti( +// WasmExecuteMsg[] memory executeMsgs +// ) payable external returns (bytes[] memory responses); +// ``` +func (p precompileWasm) executeMulti( + start OnRunStartResult, + caller gethcommon.Address, + readOnly bool, +) (bz []byte, err error) { + method, args, ctx := start.Method, start.Args, start.CacheCtx + defer func() { + if err != nil { + err = ErrMethodCalled(method, err) + } + }() + if err := assertNotReadonlyTx(readOnly, method); err != nil { + return nil, err + } + + wasmExecMsgs, err := p.parseArgsWasmExecuteMulti(args) + if err != nil { + err = ErrInvalidArgs(err) + return + } + callerBech32 := eth.EthAddrToNibiruAddr(caller) + + var responses [][]byte + for i, m := range wasmExecMsgs { + wasmContract, e := sdk.AccAddressFromBech32(m.ContractAddr) + if e != nil { + err = fmt.Errorf("Execute failed at index %d: %w", i, e) + return + } + msgArgsCopy := wasm.RawContractMessage(m.MsgArgs) + if e := msgArgsCopy.ValidateBasic(); e != nil { + err = fmt.Errorf("Execute failed at index %d: error parsing msg args: %w", i, e) + return + } + var funds sdk.Coins + for _, fund := range m.Funds { + funds = append(funds, sdk.Coin{ + Denom: fund.Denom, + Amount: sdk.NewIntFromBigInt(fund.Amount), + }) + } + respBz, e := p.Wasm.Execute(ctx, wasmContract, callerBech32, m.MsgArgs, funds) + if e != nil { + err = fmt.Errorf("Execute failed at index %d: %w", i, e) + return + } + responses = append(responses, respBz) + } + return method.Outputs.Pack(responses) +} + +// queryRaw queries the raw key-value store of a Wasm contract. This implements +// the 'queryRaw' method of Wasm.sol: +// +// ```solidity +// function queryRaw( +// string memory contractAddr, +// bytes memory key +// ) external view returns (bytes memory response); +// ``` +// +// Parameters: +// - ctx: The SDK context for the query +// - method: The ABI method being called +// - args: The arguments passed to the method +// - contract: The EVM contract context +// +// Returns: +// - bz: The encoded raw data stored at the queried key +// - err: Any error that occurred during the query +func (p precompileWasm) queryRaw( + start OnRunStartResult, + contract *vm.Contract, +) (bz []byte, err error) { + method, args, ctx := start.Method, start.Args, start.CacheCtx + defer func() { + if err != nil { + err = ErrMethodCalled(method, err) + } + }() + if err := assertContractQuery(contract); err != nil { + return bz, err + } + + if e := assertNumArgs(args, 2); e != nil { + err = e + return + } + + argIdx := 0 + wasmContract, e := parseArgContractAddr(args[argIdx]) + if e != nil { + err = e + return + } + + argIdx++ + key, ok := args[argIdx].([]byte) + if !ok { + err = ErrArgTypeValidation("bytes req", args[argIdx]) + return + } + + respBz := p.Wasm.QueryRaw(ctx, wasmContract, []byte(key)) + return method.Outputs.Pack(respBz) +} diff --git a/x/evm/precompile/wasm_parse.go b/x/evm/precompile/wasm_parse.go new file mode 100644 index 000000000..270050798 --- /dev/null +++ b/x/evm/precompile/wasm_parse.go @@ -0,0 +1,240 @@ +package precompile + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + wasm "github.com/CosmWasm/wasmd/x/wasm/types" +) + +// WasmBankCoin is a naked struct for the "BankCoin" type from Wasm.sol. +// The ABI parser requires an unnamed strict, so this type is only used in tests. +type WasmBankCoin struct { + Denom string `json:"denom"` + Amount *big.Int `json:"amount"` +} + +// Parses [sdk.Coins] from a "BankCoin[]" solidity argument: +// +// ```solidity +// BankCoin[] memory funds +// ``` +func parseFundsArg(arg any) (funds sdk.Coins, err error) { + if arg == nil { + return funds, nil + } + + raw, ok := arg.([]struct { + Denom string `json:"denom"` + Amount *big.Int `json:"amount"` + }) + + if !ok { + return funds, ErrArgTypeValidation("BankCoin[] funds", arg) + } + + for _, coin := range raw { + funds = append( + funds, + // Favor the sdk.Coin constructor over sdk.NewCoin because sdk.NewCoin + // is not panic-safe. Validation will be handled when the coin is used + // as an argument during the execution of a transaction. + sdk.Coin{ + Denom: coin.Denom, + Amount: sdk.NewIntFromBigInt(coin.Amount), + }, + ) + } + return funds, nil +} + +// Parses [sdk.AccAddress] from a "string" solidity argument: +func parseArgContractAddr(arg any) (addr sdk.AccAddress, err error) { + addrStr, ok := arg.(string) + if !ok { + err = ErrArgTypeValidation("string contractAddr", arg) + return + } + addr, err = sdk.AccAddressFromBech32(addrStr) + if err != nil { + err = fmt.Errorf("%w: %s", + ErrArgTypeValidation("string contractAddr", arg), err, + ) + return + } + return addr, nil +} + +func (p precompileWasm) parseArgsWasmInstantiate(args []any, sender string) ( + txMsg wasm.MsgInstantiateContract, + err error, +) { + if e := assertNumArgs(args, 5); e != nil { + err = e + return + } + + argIdx := 0 + admin, ok := args[argIdx].(string) + if !ok { + err = ErrArgTypeValidation("string admin", args[argIdx]) + return + } + + argIdx++ + codeID, ok := args[argIdx].(uint64) + if !ok { + err = ErrArgTypeValidation("uint64 codeID", args[argIdx]) + return + } + + argIdx++ + msgArgs, ok := args[argIdx].([]byte) + if !ok { + err = ErrArgTypeValidation("bytes msgArgs", args[argIdx]) + return + } + + argIdx++ + label, ok := args[argIdx].(string) + if !ok { + err = ErrArgTypeValidation("string label", args[argIdx]) + return + } + + argIdx++ + funds, e := parseFundsArg(args[argIdx]) + if e != nil { + err = e + return + } + + txMsg = wasm.MsgInstantiateContract{ + Sender: sender, + CodeID: codeID, + Label: label, + Msg: msgArgs, + Funds: funds, + } + if len(admin) > 0 { + txMsg.Admin = admin + } + return txMsg, txMsg.ValidateBasic() +} + +func (p precompileWasm) parseArgsWasmExecute(args []any) ( + wasmContract sdk.AccAddress, + msgArgs []byte, + funds sdk.Coins, + err error, +) { + if e := assertNumArgs(args, 3); e != nil { + err = e + return + } + + // contract address + argIdx := 0 + contractAddrStr, ok := args[argIdx].(string) + if !ok { + err = ErrArgTypeValidation("string contractAddr", args[argIdx]) + return + } + contractAddr, err := sdk.AccAddressFromBech32(contractAddrStr) + if err != nil { + err = fmt.Errorf("%w: %s", + ErrArgTypeValidation("string contractAddr", args[argIdx]), err, + ) + return + } + + // msg args + argIdx++ + msgArgs, ok = args[argIdx].([]byte) + if !ok { + err = ErrArgTypeValidation("bytes msgArgs", args[argIdx]) + return + } + msgArgsCopy := wasm.RawContractMessage(msgArgs) + if e := msgArgsCopy.ValidateBasic(); e != nil { + err = ErrArgTypeValidation(e.Error(), args[argIdx]) + return + } + + // funds + argIdx++ + funds, e := parseFundsArg(args[argIdx]) + if e != nil { + err = ErrArgTypeValidation(e.Error(), args[argIdx]) + return + } + + return contractAddr, msgArgs, funds, nil +} + +func (p precompileWasm) parseArgsWasmQuery(args []any) ( + wasmContract sdk.AccAddress, + req wasm.RawContractMessage, + err error, +) { + if e := assertNumArgs(args, 2); e != nil { + err = e + return + } + + argsIdx := 0 + wasmContract, e := parseArgContractAddr(args[argsIdx]) + if e != nil { + err = e + return + } + + argsIdx++ + reqBz, ok := args[argsIdx].([]byte) + if !ok { + err = ErrArgTypeValidation("bytes req", args[argsIdx]) + return + } + req = wasm.RawContractMessage(reqBz) + if e := req.ValidateBasic(); e != nil { + err = e + return + } + + return wasmContract, req, nil +} + +func (p precompileWasm) parseArgsWasmExecuteMulti(args []any) ( + wasmExecMsgs []struct { + ContractAddr string `json:"contractAddr"` + MsgArgs []byte `json:"msgArgs"` + Funds []struct { + Denom string `json:"denom"` + Amount *big.Int `json:"amount"` + } `json:"funds"` + }, + err error, +) { + if e := assertNumArgs(args, 1); e != nil { + err = e + return + } + + arg := args[0] + execMsgs, ok := arg.([]struct { + ContractAddr string `json:"contractAddr"` + MsgArgs []byte `json:"msgArgs"` + Funds []struct { + Denom string `json:"denom"` + Amount *big.Int `json:"amount"` + } `json:"funds"` + }) + if !ok { + err = ErrArgTypeValidation("BankCoin[] funds", arg) + return + } + + return execMsgs, nil +} diff --git a/x/evm/precompile/wasm_test.go b/x/evm/precompile/wasm_test.go new file mode 100644 index 000000000..6698e6439 --- /dev/null +++ b/x/evm/precompile/wasm_test.go @@ -0,0 +1,772 @@ +package precompile_test + +import ( + "encoding/json" + "fmt" + "math/big" + "testing" + + "cosmossdk.io/math" + wasm "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile/test" + tokenfactory "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" +) + +// rough gas limits for wasm execution - used in tests only +const ( + WasmGasLimitQuery uint64 = 200_000 + WasmGasLimitExecute uint64 = 1_000_000 +) + +type WasmSuite struct { + suite.Suite +} + +func TestWasmSuite(t *testing.T) { + suite.Run(t, new(WasmSuite)) +} + +func (s *WasmSuite) TestInstantiate() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + + test.SetupWasmContracts(&deps, &s.Suite) + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_instantiate), + "", // admin + uint64(1), // codeId + []byte(`{}`), // instantiateMsg + "some non-empty label", // label + []precompile.WasmBankCoin{}, // funds + ) + s.Require().NoError(err) + + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + true, + contractInput, + WasmGasLimitExecute, + ) + + s.Require().NoError(err) + s.Require().NotEmpty(ethTxResp.Ret) + + vals, err := embeds.SmartContract_Wasm.ABI.Unpack( + string(precompile.WasmMethod_instantiate), + ethTxResp.Ret, + ) + s.Require().NoError(err) + s.Require().NotEmpty(vals[0].(string)) +} + +func (s *WasmSuite) TestExecute() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[0] // nibi_stargate.wasm + + s.Run("create denom", func() { + msgArgsBz := []byte(` + { + "create_denom": { + "subdenom": "ETH" + } + } + `) + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_execute), + wasmContract.String(), + msgArgsBz, + []precompile.WasmBankCoin{}, + ) + s.Require().NoError(err) + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + true, + contractInput, + WasmGasLimitExecute, + ) + s.Require().NoError(err) + s.Require().NotEmpty(ethTxResp.Ret) + }) + + s.Run("mint tokens", func() { + coinDenom := tokenfactory.TFDenom{ + Creator: wasmContract.String(), + Subdenom: "ETH", + }.Denom().String() + msgArgsBz := []byte(fmt.Sprintf(` + { + "mint": { + "coin": { "amount": "69420", "denom": "%s" }, + "mint_to": "%s" + } + } + `, coinDenom, deps.Sender.NibiruAddr)) + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_execute), + wasmContract.String(), + msgArgsBz, + []precompile.WasmBankCoin{}, + ) + s.Require().NoError(err) + + evmObj, _ = deps.NewEVM() + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + true, + contractInput, + WasmGasLimitExecute, + ) + + s.Require().NoError(err) + s.Require().NotEmpty(ethTxResp.Ret) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, coinDenom, deps.Sender.EthAddr, big.NewInt(69_420), "expect 69420 balance") + }) +} + +func (s *WasmSuite) TestExecuteMulti() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[1] // hello_world_counter.wasm + + // count = 0 + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 0) + // count += 2 + test.IncrementWasmCounterWithExecuteMulti( + &s.Suite, &deps, evmObj, wasmContract, 2, true) + // count = 2 + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 2) + // count += 67 + test.IncrementWasmCounterWithExecuteMulti( + &s.Suite, &deps, evmObj, wasmContract, 67, true) + // count = 69 + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 69) +} + +func (s *WasmSuite) TestQueryRaw() { + deps := evmtest.NewTestDeps() + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[1] // hello_world_counter.wasm + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_queryRaw), + wasmContract.String(), + []byte(`state`), + ) + s.Require().NoError(err) + + evmObj, _ := deps.NewEVM() + queryResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + false, // commit + contractInput, + WasmGasLimitQuery, + ) + + s.Require().NoError(err) + s.Require().NotEmpty(queryResp.Ret) + + var respBz []byte + err = embeds.SmartContract_Wasm.ABI.UnpackIntoInterface( + &respBz, + string(precompile.WasmMethod_queryRaw), + queryResp.Ret, + ) + s.Require().NoError(err, "ethTxResp: %s", queryResp) + + var typedResp test.QueryMsgCountResp + s.NoError(json.Unmarshal(respBz, &typedResp)) + s.EqualValues(0, typedResp.Count) + s.EqualValues(deps.Sender.NibiruAddr.String(), typedResp.Owner) +} + +func (s *WasmSuite) TestQuerySmart() { + deps := evmtest.NewTestDeps() + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[1] // hello_world_counter.wasm + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_query), + wasmContract.String(), + []byte(`{"count": {}}`), + ) + s.Require().NoError(err) + + evmObj, _ := deps.NewEVM() + queryResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + false, // commit + contractInput, + WasmGasLimitQuery, + ) + + s.Require().NoError(err) + s.Require().NotEmpty(queryResp.Ret) + + var respBz []byte + err = embeds.SmartContract_Wasm.ABI.UnpackIntoInterface( + &respBz, + string(precompile.WasmMethod_query), + queryResp.Ret, + ) + s.Require().NoError(err, "ethTxResp: %s", queryResp) + + var typedResp test.QueryMsgCountResp + s.Require().NoError(json.Unmarshal(respBz, &typedResp)) + s.Require().EqualValues(0, typedResp.Count) + s.Require().EqualValues(deps.Sender.NibiruAddr.String(), typedResp.Owner) +} + +func (s *WasmSuite) TestSadArgsCount() { + nonsenseArgs := []any{"nonsense", "args here", "to see if", "precompile is", "called"} + testcases := []struct { + name string + methodName precompile.PrecompileMethod + callArgs []any + wantError string + }{ + { + name: "execute", + methodName: precompile.WasmMethod_execute, + callArgs: nonsenseArgs, + wantError: "argument count mismatch: got 5 for 3", + }, + { + name: "executeMulti", + methodName: precompile.WasmMethod_executeMulti, + callArgs: nonsenseArgs, + wantError: "argument count mismatch: got 5 for 1", + }, + { + name: "query", + methodName: precompile.WasmMethod_query, + callArgs: nonsenseArgs, + wantError: "argument count mismatch: got 5 for 2", + }, + { + name: "queryRaw", + methodName: precompile.WasmMethod_queryRaw, + callArgs: nonsenseArgs, + wantError: "argument count mismatch: got 5 for 2", + }, + { + name: "instantiate", + methodName: precompile.WasmMethod_instantiate, + callArgs: nonsenseArgs[:4], + wantError: "argument count mismatch: got 4 for 5", + }, + { + name: "invalid method name", + methodName: "not_a_method", + callArgs: nonsenseArgs, + wantError: "method 'not_a_method' not found", + }, + } + + abi := embeds.SmartContract_Wasm.ABI + for _, tc := range testcases { + s.Run(tc.name, func() { + callArgs := tc.callArgs + _, err := abi.Pack( + string(tc.methodName), + callArgs..., + ) + s.Require().ErrorContains(err, tc.wantError) + }) + } +} + +func (s *WasmSuite) TestSadArgsExecute() { + methodName := precompile.WasmMethod_execute + contractAddr := testutil.AccAddress().String() + wasmContractMsg := []byte(` + { "create_denom": { + "subdenom": "ETH" + } + } + `) + { + wasmMsg := wasm.RawContractMessage(wasmContractMsg) + s.Require().NoError(wasmMsg.ValidateBasic()) + } + + testcases := []struct { + name string + methodName precompile.PrecompileMethod + callArgs []any + wantError string + }{ + { + name: "valid arg types, should get VM error", + methodName: methodName, + callArgs: []any{ + // contractAddr + contractAddr, + // msgArgBz + wasmContractMsg, + // funds + []precompile.WasmBankCoin{}, + }, + wantError: "execute method called", + }, + { + name: "contractAddr", + methodName: methodName, + callArgs: []any{ + // contractAddr + contractAddr + "malformed", // mess up bech32 + // msgArgBz + wasmContractMsg, + // funds + []precompile.WasmBankCoin{}, + }, + wantError: "decoding bech32 failed", + }, + { + name: "funds populated", + methodName: methodName, + callArgs: []any{ + // contractAddr + contractAddr, + // msgArgBz + []byte(`[]`), + // funds + []precompile.WasmBankCoin{ + { + Denom: "x-123a!$", + Amount: big.NewInt(123), + }, + { + Denom: "xyz", + Amount: big.NewInt(456), + }, + }, + }, + wantError: "no such contract", + }, + } + + abi := embeds.SmartContract_Wasm.ABI + for _, tc := range testcases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + + contractInput, err := abi.Pack( + string(tc.methodName), + tc.callArgs..., + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + true, + contractInput, + WasmGasLimitExecute, + ) + + s.Require().ErrorContains(err, tc.wantError, "ethTxResp %v", ethTxResp) + }) + } +} + +type WasmExecuteMsg struct { + ContractAddr string `json:"contractAddr"` + MsgArgs []byte `json:"msgArgs"` + Funds []precompile.WasmBankCoin `json:"funds"` +} + +func (s *WasmSuite) TestExecuteMultiValidation() { + deps := evmtest.NewTestDeps() + + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(100))), + )) + + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[1] // hello_world_counter.wasm + + validMsgArgsBz := []byte(`{"increment": {}}`) // Valid increment message + invalidMsgArgsBz := []byte(`{"invalid": "json"}`) // Invalid message format + + var emptyFunds []precompile.WasmBankCoin + validFunds := []precompile.WasmBankCoin{{ + Denom: evm.EVMBankDenom, + Amount: big.NewInt(100), + }} + invalidFunds := []precompile.WasmBankCoin{{ + Denom: "invalid!denom", + Amount: big.NewInt(100), + }} + + testCases := []struct { + name string + executeMsgs []WasmExecuteMsg + wantError string + }{ + { + name: "valid - single message", + executeMsgs: []WasmExecuteMsg{ + { + ContractAddr: wasmContract.String(), + MsgArgs: validMsgArgsBz, + Funds: emptyFunds, + }, + }, + wantError: "", + }, + { + name: "valid - multiple messages", + executeMsgs: []WasmExecuteMsg{ + { + ContractAddr: wasmContract.String(), + MsgArgs: validMsgArgsBz, + Funds: validFunds, + }, + { + ContractAddr: wasmContract.String(), + MsgArgs: validMsgArgsBz, + Funds: emptyFunds, + }, + }, + wantError: "", + }, + { + name: "invalid - malformed contract address", + executeMsgs: []WasmExecuteMsg{ + { + ContractAddr: "invalid-address", + MsgArgs: validMsgArgsBz, + Funds: emptyFunds, + }, + }, + wantError: "decoding bech32 failed", + }, + { + name: "invalid - malformed message args", + executeMsgs: []WasmExecuteMsg{ + { + ContractAddr: wasmContract.String(), + MsgArgs: invalidMsgArgsBz, + Funds: emptyFunds, + }, + }, + wantError: "unknown variant", + }, + { + name: "invalid - malformed funds", + executeMsgs: []WasmExecuteMsg{ + { + ContractAddr: wasmContract.String(), + MsgArgs: validMsgArgsBz, + Funds: invalidFunds, + }, + }, + wantError: "invalid coins", + }, + { + name: "invalid - second message fails validation", + executeMsgs: []WasmExecuteMsg{ + { + ContractAddr: wasmContract.String(), + MsgArgs: validMsgArgsBz, + Funds: emptyFunds, + }, + { + ContractAddr: wasmContract.String(), + MsgArgs: invalidMsgArgsBz, + Funds: emptyFunds, + }, + }, + wantError: "unknown variant", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_executeMulti), + tc.executeMsgs, + ) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + true, + contractInput, + WasmGasLimitExecute, + ) + + if tc.wantError != "" { + s.Require().ErrorContains(err, tc.wantError) + return + } + s.Require().NoError(err) + s.NotNil(ethTxResp) + s.NotEmpty(ethTxResp.Ret) + }) + } +} + +// TestExecuteMultiPartialExecution ensures that no state changes occur if any message +// in the batch fails validation +func (s *WasmSuite) TestExecuteMultiPartialExecution() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[1] // hello_world_counter.wasm + + // First verify initial state is 0 + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 0) + + // Create a batch where the second message will fail validation + executeMsgs := []WasmExecuteMsg{ + { + ContractAddr: wasmContract.String(), + MsgArgs: []byte(`{"increment": {}}`), + Funds: []precompile.WasmBankCoin{}, + }, + { + ContractAddr: wasmContract.String(), + MsgArgs: []byte(`{"invalid": "json"}`), // This will fail validation + Funds: []precompile.WasmBankCoin{}, + }, + } + + contractInput, err := embeds.SmartContract_Wasm.ABI.Pack( + string(precompile.WasmMethod_executeMulti), + executeMsgs, + ) + s.Require().NoError(err) + ethTxResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &precompile.PrecompileAddr_Wasm, + true, + contractInput, + WasmGasLimitExecute, + ) + + // Verify that the call failed + s.Require().Error(err, "ethTxResp: ", ethTxResp) + s.Require().Contains(err.Error(), "unknown variant") + + // Verify that no state changes occurred + test.AssertWasmCounterState(&s.Suite, deps, wasmContract, 0) +} + +// TestDirtyStateAttack4 +// 1. Deploy a simple wasm contract that bank transfers NIBI to a recipient (Alice) +// 2. Calls the test contract +// a. call the wasm precompile which calls the wasm contract with a bank transfer +// +// INITIAL STATE: +// - Test contract funds: 10 NIBI +// CONTRACT CALL: +// - Sends 1 NIBI to Alice via wasm precompile +// EXPECTED: +// - Test contract funds: 9 NIBI +// - Alice: 1 NIBI +func (s *WasmSuite) TestWasmPrecompileDirtyStateAttack4() { + deps := evmtest.NewTestDeps() + + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[2] // bank_transfer.wasm + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestDirtyStateAttack4, + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.Run("Send 10 NIBI to test contract manually", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + eth.EthAddrToNibiruAddr(testContractAddr), + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))), + )) + }) + + alice := evmtest.NewEthPrivAcc() + + s.Run("call test contract", func() { + msgArgsBz := []byte(fmt.Sprintf(` + { + "bank_transfer": { + "recipient": "%s" + } + } + `, alice.NibiruAddr)) + contractInput, err := embeds.SmartContract_TestDirtyStateAttack4.ABI.Pack( + "attack", + wasmContract.String(), + msgArgsBz, + ) + s.Require().NoError(err) + + evmObj, _ := deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + + balanceAlice := deps.App.BankKeeper.GetBalance(deps.Ctx, alice.NibiruAddr, evm.EVMBankDenom) + s.Require().Equal(balanceAlice.Amount.BigInt(), big.NewInt(1e6)) + + balanceTestContract := deps.App.BankKeeper.GetBalance(deps.Ctx, eth.EthAddrToNibiruAddr(testContractAddr), evm.EVMBankDenom) + s.Require().Equal(balanceTestContract.Amount.BigInt(), big.NewInt(9e6)) + }) +} + +// TestDirtyStateAttack5 +// 1. Deploy a simple wasm contract that stakes NIBI +// 2. Calls the test contract +// a. call the wasm precompile which calls the wasm contract that stakes 5 NIBI +// +// INITIAL STATE: +// - Test contract funds: 10 NIBI +// CONTRACT CALL: +// - Sends 5 NIBI to the wasm contract +// - The wasm contract stakes 5 NIBI +// EXPECTED: +// - Test contract funds: 5 NIBI +// - Staked NIBI from wasm contract: 5 NIBI +// - Wasm contract: 0 NIBI +func (s *WasmSuite) TestWasmPrecompileDirtyStateAttack5() { + deps := evmtest.NewTestDeps() + + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + wasmContract := wasmContracts[3] // staking.wasm + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestDirtyStateAttack5, + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.Run("Mint 10 NIBI to test contract", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + eth.EthAddrToNibiruAddr(testContractAddr), + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))), + )) + }) + + validator := evmtest.NewEthPrivAcc() + s.Run("create validator", func() { + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + validator.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))), + )) + + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(validator.NibiruAddr), + validator.PrivKey.PubKey(), + sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)), + stakingtypes.NewDescription("validator0", "", "", "", ""), + stakingtypes.NewCommissionRates(sdk.NewDec(1), sdk.NewDec(1), sdk.NewDec(1)), + math.OneInt(), + ) + s.Require().NoError(err) + + stakingMsgServer := stakingkeeper.NewMsgServerImpl(deps.App.StakingKeeper) + resp, err := stakingMsgServer.CreateValidator(deps.Ctx, createValMsg) + s.Require().NoError(err) + s.Require().NotNil(resp) + }) + + s.Run("call test contract", func() { + msgArgsBz := []byte(fmt.Sprintf(`{"run": {"validator": "%s"}}`, sdk.ValAddress(validator.NibiruAddr).String())) + contractInput, err := embeds.SmartContract_TestDirtyStateAttack5.ABI.Pack( + "attack", + wasmContract.String(), + msgArgsBz, + ) + s.Require().NoError(err) + + evmObj, _ := deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + ) + s.Require().NoError(err) + + testContractBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, eth.EthAddrToNibiruAddr(testContractAddr), evm.EVMBankDenom) + s.Require().Equal(testContractBalance.Amount.BigInt(), big.NewInt(5e6)) + + wasmContractBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, wasmContract, evm.EVMBankDenom) + s.Require().Equal(wasmContractBalance.Amount.BigInt(), big.NewInt(0)) + + delegations := deps.App.StakingKeeper.GetAllDelegatorDelegations(deps.Ctx, wasmContract) + s.Require().Equal(len(delegations), 1) + s.Require().Equal(sdk.ValAddress(validator.NibiruAddr).String(), delegations[0].ValidatorAddress) + + // after converting the wasm contract address to an eth address, check the balances + wasmContractEthAddr := eth.NibiruAddrToEthAddr(wasmContract) + balance := deps.App.BankKeeper.GetBalance(deps.Ctx, eth.EthAddrToNibiruAddr(wasmContractEthAddr), evm.EVMBankDenom) + s.Require().Equal(big.NewInt(0), balance.Amount.BigInt()) + }) +} diff --git a/x/evm/query.go b/x/evm/query.go new file mode 100644 index 000000000..1a5df4cb2 --- /dev/null +++ b/x/evm/query.go @@ -0,0 +1,143 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "fmt" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common" +) + +func (m QueryTraceTxRequest) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, msg := range m.Predecessors { + if err := msg.UnpackInterfaces(unpacker); err != nil { + return err + } + } + return m.Msg.UnpackInterfaces(unpacker) +} + +func (m QueryTraceBlockRequest) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { + for _, msg := range m.Txs { + if err := msg.UnpackInterfaces(unpacker); err != nil { + return err + } + } + return nil +} + +func (req *QueryEthAccountRequest) Validate() (isBech32 bool, err error) { + if req == nil { + return isBech32, common.ErrNilGrpcMsg + } + + ethAddrErr := eth.ValidateAddress(req.Address) + _, bech32AddrErr := sdk.AccAddressFromBech32(req.Address) + + switch { + case ethAddrErr == nil: + isBech32 = false + return isBech32, nil + case bech32AddrErr == nil: + isBech32 = true + return isBech32, nil + default: + return isBech32, status.Error( + codes.InvalidArgument, + fmt.Errorf( + "could not parse address as Nibiru Bech32 or Ethereum hexadecimal: {{ Ethereum error: %w, bech32 error: %w }}", ethAddrErr, bech32AddrErr, + ).Error(), + ) + } +} + +func (req *QueryValidatorAccountRequest) Validate() ( + consAddr sdk.ConsAddress, err error, +) { + if req == nil { + return consAddr, status.Error(codes.InvalidArgument, "empty request") + } + + consAddr, err = sdk.ConsAddressFromBech32(req.ConsAddress) + if err != nil { + return consAddr, status.Error( + codes.InvalidArgument, err.Error(), + ) + } + return consAddr, nil +} + +func (req *QueryBalanceRequest) Validate() error { + if req == nil { + return common.ErrNilGrpcMsg + } + + if err := eth.ValidateAddress(req.Address); err != nil { + return status.Error( + codes.InvalidArgument, + ErrZeroAddress.Error(), + ) + } + return nil +} + +func (req *QueryStorageRequest) Validate() error { + if req == nil { + return common.ErrNilGrpcMsg + } + if err := eth.ValidateAddress(req.Address); err != nil { + return status.Error( + codes.InvalidArgument, + ErrZeroAddress.Error(), + ) + } + return nil +} + +func (req *QueryCodeRequest) Validate() error { + if req == nil { + return common.ErrNilGrpcMsg + } + + if err := eth.ValidateAddress(req.Address); err != nil { + return status.Error( + codes.InvalidArgument, + ErrZeroAddress.Error(), + ) + } + return nil +} + +func (req *EthCallRequest) Validate() error { + if req == nil { + return common.ErrNilGrpcMsg + } + return nil +} + +func (req *QueryTraceTxRequest) Validate() error { + if req == nil { + return common.ErrNilGrpcMsg + } + + if req.TraceConfig != nil && req.TraceConfig.Limit < 0 { + return status.Errorf(codes.InvalidArgument, "output limit cannot be negative, got %d", req.TraceConfig.Limit) + } + return nil +} + +func (req *QueryTraceBlockRequest) Validate() error { + if req == nil { + return common.ErrNilGrpcMsg + } + + if req.TraceConfig != nil && req.TraceConfig.Limit < 0 { + return status.Errorf(codes.InvalidArgument, "output limit cannot be negative, got %d", req.TraceConfig.Limit) + } + return nil +} diff --git a/x/evm/query.pb.go b/x/evm/query.pb.go new file mode 100644 index 000000000..26db67b42 --- /dev/null +++ b/x/evm/query.pb.go @@ -0,0 +1,6229 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: eth/evm/v1/query.proto + +package evm + +import ( + context "context" + cosmossdk_io_math "cosmossdk.io/math" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + _ "google.golang.org/protobuf/types/known/timestamppb" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryEthAccountRequest is the request type for the Query/Account RPC method. +type QueryEthAccountRequest struct { + // address is the Ethereum hex address or nibi Bech32 address to query the account for. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *QueryEthAccountRequest) Reset() { *m = QueryEthAccountRequest{} } +func (m *QueryEthAccountRequest) String() string { return proto.CompactTextString(m) } +func (*QueryEthAccountRequest) ProtoMessage() {} +func (*QueryEthAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{0} +} +func (m *QueryEthAccountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEthAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEthAccountRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryEthAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEthAccountRequest.Merge(m, src) +} +func (m *QueryEthAccountRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryEthAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEthAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEthAccountRequest proto.InternalMessageInfo + +// QueryEthAccountResponse is the response type for the Query/EthAccount RPC method. +type QueryEthAccountResponse struct { + // balance is the balance of unibi (micronibi). + Balance string `protobuf:"bytes,1,opt,name=balance,proto3" json:"balance,omitempty"` + // balance_wei is the balance of wei (attoether, where NIBI is ether). + BalanceWei string `protobuf:"bytes,2,opt,name=balance_wei,json=balanceWei,proto3" json:"balance_wei,omitempty"` + // code_hash is the hex-formatted code bytes from the EOA. + CodeHash string `protobuf:"bytes,3,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"` + // nonce is the account's sequence number. + Nonce uint64 `protobuf:"varint,4,opt,name=nonce,proto3" json:"nonce,omitempty"` + // eth_address: The hexadecimal-encoded string representing the 20 byte address + // of a Nibiru EVM account. + EthAddress string `protobuf:"bytes,5,opt,name=eth_address,json=ethAddress,proto3" json:"eth_address,omitempty"` + // bech32_address is the nibi-prefixed address of the account that can receive + // bank transfers ("cosmos.bank.v1beta1.MsgSend"). + Bech32Address string `protobuf:"bytes,6,opt,name=bech32_address,json=bech32Address,proto3" json:"bech32_address,omitempty"` +} + +func (m *QueryEthAccountResponse) Reset() { *m = QueryEthAccountResponse{} } +func (m *QueryEthAccountResponse) String() string { return proto.CompactTextString(m) } +func (*QueryEthAccountResponse) ProtoMessage() {} +func (*QueryEthAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{1} +} +func (m *QueryEthAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEthAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEthAccountResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryEthAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEthAccountResponse.Merge(m, src) +} +func (m *QueryEthAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryEthAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEthAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEthAccountResponse proto.InternalMessageInfo + +func (m *QueryEthAccountResponse) GetBalance() string { + if m != nil { + return m.Balance + } + return "" +} + +func (m *QueryEthAccountResponse) GetBalanceWei() string { + if m != nil { + return m.BalanceWei + } + return "" +} + +func (m *QueryEthAccountResponse) GetCodeHash() string { + if m != nil { + return m.CodeHash + } + return "" +} + +func (m *QueryEthAccountResponse) GetNonce() uint64 { + if m != nil { + return m.Nonce + } + return 0 +} + +func (m *QueryEthAccountResponse) GetEthAddress() string { + if m != nil { + return m.EthAddress + } + return "" +} + +func (m *QueryEthAccountResponse) GetBech32Address() string { + if m != nil { + return m.Bech32Address + } + return "" +} + +// QueryValidatorAccountRequest is the request type for the +// Query/ValidatorAccount RPC method. +type QueryValidatorAccountRequest struct { + // cons_address is the validator cons address to query the account for. + ConsAddress string `protobuf:"bytes,1,opt,name=cons_address,json=consAddress,proto3" json:"cons_address,omitempty"` +} + +func (m *QueryValidatorAccountRequest) Reset() { *m = QueryValidatorAccountRequest{} } +func (m *QueryValidatorAccountRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorAccountRequest) ProtoMessage() {} +func (*QueryValidatorAccountRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{2} +} +func (m *QueryValidatorAccountRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorAccountRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidatorAccountRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorAccountRequest.Merge(m, src) +} +func (m *QueryValidatorAccountRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorAccountRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorAccountRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorAccountRequest proto.InternalMessageInfo + +// QueryValidatorAccountResponse is the response type for the +// Query/ValidatorAccount RPC method. +type QueryValidatorAccountResponse struct { + // account_address is the Nibiru address of the account in bech32 format. + AccountAddress string `protobuf:"bytes,1,opt,name=account_address,json=accountAddress,proto3" json:"account_address,omitempty"` + // sequence is the account's sequence number. + Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` + // account_number is the account number + AccountNumber uint64 `protobuf:"varint,3,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` +} + +func (m *QueryValidatorAccountResponse) Reset() { *m = QueryValidatorAccountResponse{} } +func (m *QueryValidatorAccountResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorAccountResponse) ProtoMessage() {} +func (*QueryValidatorAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{3} +} +func (m *QueryValidatorAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorAccountResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidatorAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorAccountResponse.Merge(m, src) +} +func (m *QueryValidatorAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorAccountResponse proto.InternalMessageInfo + +func (m *QueryValidatorAccountResponse) GetAccountAddress() string { + if m != nil { + return m.AccountAddress + } + return "" +} + +func (m *QueryValidatorAccountResponse) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +func (m *QueryValidatorAccountResponse) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +// QueryBalanceRequest is the request type for the Query/Balance RPC method. +type QueryBalanceRequest struct { + // address is the ethereum hex address to query the balance for. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *QueryBalanceRequest) Reset() { *m = QueryBalanceRequest{} } +func (m *QueryBalanceRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBalanceRequest) ProtoMessage() {} +func (*QueryBalanceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{4} +} +func (m *QueryBalanceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBalanceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBalanceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryBalanceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBalanceRequest.Merge(m, src) +} +func (m *QueryBalanceRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBalanceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBalanceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBalanceRequest proto.InternalMessageInfo + +// QueryBalanceResponse is the response type for the Query/Balance RPC method. +type QueryBalanceResponse struct { + // balance is the balance of the EVM denomination + Balance string `protobuf:"bytes,1,opt,name=balance,proto3" json:"balance,omitempty"` + // balance is the balance of the EVM denomination in units of wei. + BalanceWei string `protobuf:"bytes,2,opt,name=balance_wei,json=balanceWei,proto3" json:"balance_wei,omitempty"` +} + +func (m *QueryBalanceResponse) Reset() { *m = QueryBalanceResponse{} } +func (m *QueryBalanceResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBalanceResponse) ProtoMessage() {} +func (*QueryBalanceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{5} +} +func (m *QueryBalanceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBalanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBalanceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryBalanceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBalanceResponse.Merge(m, src) +} +func (m *QueryBalanceResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBalanceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBalanceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBalanceResponse proto.InternalMessageInfo + +func (m *QueryBalanceResponse) GetBalance() string { + if m != nil { + return m.Balance + } + return "" +} + +func (m *QueryBalanceResponse) GetBalanceWei() string { + if m != nil { + return m.BalanceWei + } + return "" +} + +// QueryStorageRequest is the request type for the Query/Storage RPC method. +type QueryStorageRequest struct { + // address is the ethereum hex address to query the storage state for. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // key defines the key of the storage state + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *QueryStorageRequest) Reset() { *m = QueryStorageRequest{} } +func (m *QueryStorageRequest) String() string { return proto.CompactTextString(m) } +func (*QueryStorageRequest) ProtoMessage() {} +func (*QueryStorageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{6} +} +func (m *QueryStorageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryStorageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryStorageRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryStorageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryStorageRequest.Merge(m, src) +} +func (m *QueryStorageRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryStorageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryStorageRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryStorageRequest proto.InternalMessageInfo + +// QueryStorageResponse is the response type for the Query/Storage RPC +// method. +type QueryStorageResponse struct { + // value defines the storage state value hash associated with the given key. + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *QueryStorageResponse) Reset() { *m = QueryStorageResponse{} } +func (m *QueryStorageResponse) String() string { return proto.CompactTextString(m) } +func (*QueryStorageResponse) ProtoMessage() {} +func (*QueryStorageResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{7} +} +func (m *QueryStorageResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryStorageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryStorageResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryStorageResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryStorageResponse.Merge(m, src) +} +func (m *QueryStorageResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryStorageResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryStorageResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryStorageResponse proto.InternalMessageInfo + +func (m *QueryStorageResponse) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +// QueryCodeRequest is the request type for the Query/Code RPC method. +type QueryCodeRequest struct { + // address is the ethereum hex address to query the code for. + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` +} + +func (m *QueryCodeRequest) Reset() { *m = QueryCodeRequest{} } +func (m *QueryCodeRequest) String() string { return proto.CompactTextString(m) } +func (*QueryCodeRequest) ProtoMessage() {} +func (*QueryCodeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{8} +} +func (m *QueryCodeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCodeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCodeRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryCodeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCodeRequest.Merge(m, src) +} +func (m *QueryCodeRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryCodeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCodeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCodeRequest proto.InternalMessageInfo + +// QueryCodeResponse is the response type for the Query/Code RPC +// method. +type QueryCodeResponse struct { + // code represents the code bytes from an ethereum address. + Code []byte `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` +} + +func (m *QueryCodeResponse) Reset() { *m = QueryCodeResponse{} } +func (m *QueryCodeResponse) String() string { return proto.CompactTextString(m) } +func (*QueryCodeResponse) ProtoMessage() {} +func (*QueryCodeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{9} +} +func (m *QueryCodeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCodeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCodeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryCodeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCodeResponse.Merge(m, src) +} +func (m *QueryCodeResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryCodeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCodeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCodeResponse proto.InternalMessageInfo + +func (m *QueryCodeResponse) GetCode() []byte { + if m != nil { + return m.Code + } + return nil +} + +// QueryTxLogsRequest is the request type for the Query/TxLogs RPC method. +type QueryTxLogsRequest struct { + // hash is the ethereum transaction hex hash to query the logs for. + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + // pagination defines an optional pagination for the request. + Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryTxLogsRequest) Reset() { *m = QueryTxLogsRequest{} } +func (m *QueryTxLogsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryTxLogsRequest) ProtoMessage() {} +func (*QueryTxLogsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{10} +} +func (m *QueryTxLogsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTxLogsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTxLogsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryTxLogsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTxLogsRequest.Merge(m, src) +} +func (m *QueryTxLogsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTxLogsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTxLogsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTxLogsRequest proto.InternalMessageInfo + +// QueryTxLogsResponse is the response type for the Query/TxLogs RPC method. +type QueryTxLogsResponse struct { + // logs represents the ethereum logs generated from the given transaction. + Logs []*Log `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"` + // pagination defines the pagination in the response. + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryTxLogsResponse) Reset() { *m = QueryTxLogsResponse{} } +func (m *QueryTxLogsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryTxLogsResponse) ProtoMessage() {} +func (*QueryTxLogsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{11} +} +func (m *QueryTxLogsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTxLogsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTxLogsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryTxLogsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTxLogsResponse.Merge(m, src) +} +func (m *QueryTxLogsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTxLogsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTxLogsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTxLogsResponse proto.InternalMessageInfo + +func (m *QueryTxLogsResponse) GetLogs() []*Log { + if m != nil { + return m.Logs + } + return nil +} + +func (m *QueryTxLogsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + +// QueryParamsRequest defines the request type for querying x/evm parameters. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{12} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse defines the response type for querying x/evm parameters. +type QueryParamsResponse struct { + // params define the evm module parameters. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{13} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// EthCallRequest defines EthCall request +type EthCallRequest struct { + // args uses the same json format as the json rpc api. + Args []byte `protobuf:"bytes,1,opt,name=args,proto3" json:"args,omitempty"` + // gas_cap defines the default gas cap to be used + GasCap uint64 `protobuf:"varint,2,opt,name=gas_cap,json=gasCap,proto3" json:"gas_cap,omitempty"` + // proposer_address of the requested block in hex format + ProposerAddress github_com_cosmos_cosmos_sdk_types.ConsAddress `protobuf:"bytes,3,opt,name=proposer_address,json=proposerAddress,proto3,casttype=github.com/cosmos/cosmos-sdk/types.ConsAddress" json:"proposer_address,omitempty"` + // chain_id is the eip155 chain id parsed from the requested block header + ChainId int64 `protobuf:"varint,4,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (m *EthCallRequest) Reset() { *m = EthCallRequest{} } +func (m *EthCallRequest) String() string { return proto.CompactTextString(m) } +func (*EthCallRequest) ProtoMessage() {} +func (*EthCallRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{14} +} +func (m *EthCallRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EthCallRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EthCallRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EthCallRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_EthCallRequest.Merge(m, src) +} +func (m *EthCallRequest) XXX_Size() int { + return m.Size() +} +func (m *EthCallRequest) XXX_DiscardUnknown() { + xxx_messageInfo_EthCallRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_EthCallRequest proto.InternalMessageInfo + +func (m *EthCallRequest) GetArgs() []byte { + if m != nil { + return m.Args + } + return nil +} + +func (m *EthCallRequest) GetGasCap() uint64 { + if m != nil { + return m.GasCap + } + return 0 +} + +func (m *EthCallRequest) GetProposerAddress() github_com_cosmos_cosmos_sdk_types.ConsAddress { + if m != nil { + return m.ProposerAddress + } + return nil +} + +func (m *EthCallRequest) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +// EstimateGasResponse defines EstimateGas response +type EstimateGasResponse struct { + // gas returns the estimated gas + Gas uint64 `protobuf:"varint,1,opt,name=gas,proto3" json:"gas,omitempty"` +} + +func (m *EstimateGasResponse) Reset() { *m = EstimateGasResponse{} } +func (m *EstimateGasResponse) String() string { return proto.CompactTextString(m) } +func (*EstimateGasResponse) ProtoMessage() {} +func (*EstimateGasResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{15} +} +func (m *EstimateGasResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EstimateGasResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EstimateGasResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EstimateGasResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EstimateGasResponse.Merge(m, src) +} +func (m *EstimateGasResponse) XXX_Size() int { + return m.Size() +} +func (m *EstimateGasResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EstimateGasResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EstimateGasResponse proto.InternalMessageInfo + +func (m *EstimateGasResponse) GetGas() uint64 { + if m != nil { + return m.Gas + } + return 0 +} + +// QueryTraceTxRequest defines TraceTx request +type QueryTraceTxRequest struct { + // msg is the MsgEthereumTx for the requested transaction + Msg *MsgEthereumTx `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` + // trace_config holds extra parameters to trace functions. + TraceConfig *TraceConfig `protobuf:"bytes,3,opt,name=trace_config,json=traceConfig,proto3" json:"trace_config,omitempty"` + // predecessors is an array of transactions included in the same block + // need to be replayed first to get correct context for tracing. + Predecessors []*MsgEthereumTx `protobuf:"bytes,4,rep,name=predecessors,proto3" json:"predecessors,omitempty"` + // block_number of requested transaction + BlockNumber int64 `protobuf:"varint,5,opt,name=block_number,json=blockNumber,proto3" json:"block_number,omitempty"` + // block_hash of requested transaction + BlockHash string `protobuf:"bytes,6,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` + // block_time of requested transaction + BlockTime time.Time `protobuf:"bytes,7,opt,name=block_time,json=blockTime,proto3,stdtime" json:"block_time"` + // proposer_address is the proposer of the requested block + ProposerAddress github_com_cosmos_cosmos_sdk_types.ConsAddress `protobuf:"bytes,8,opt,name=proposer_address,json=proposerAddress,proto3,casttype=github.com/cosmos/cosmos-sdk/types.ConsAddress" json:"proposer_address,omitempty"` + // chain_id is the the eip155 chain id parsed from the requested block header + ChainId int64 `protobuf:"varint,9,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // block_max_gas of the block of the requested transaction + BlockMaxGas int64 `protobuf:"varint,10,opt,name=block_max_gas,json=blockMaxGas,proto3" json:"block_max_gas,omitempty"` +} + +func (m *QueryTraceTxRequest) Reset() { *m = QueryTraceTxRequest{} } +func (m *QueryTraceTxRequest) String() string { return proto.CompactTextString(m) } +func (*QueryTraceTxRequest) ProtoMessage() {} +func (*QueryTraceTxRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{16} +} +func (m *QueryTraceTxRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTraceTxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTraceTxRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryTraceTxRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTraceTxRequest.Merge(m, src) +} +func (m *QueryTraceTxRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTraceTxRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTraceTxRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTraceTxRequest proto.InternalMessageInfo + +func (m *QueryTraceTxRequest) GetMsg() *MsgEthereumTx { + if m != nil { + return m.Msg + } + return nil +} + +func (m *QueryTraceTxRequest) GetTraceConfig() *TraceConfig { + if m != nil { + return m.TraceConfig + } + return nil +} + +func (m *QueryTraceTxRequest) GetPredecessors() []*MsgEthereumTx { + if m != nil { + return m.Predecessors + } + return nil +} + +func (m *QueryTraceTxRequest) GetBlockNumber() int64 { + if m != nil { + return m.BlockNumber + } + return 0 +} + +func (m *QueryTraceTxRequest) GetBlockHash() string { + if m != nil { + return m.BlockHash + } + return "" +} + +func (m *QueryTraceTxRequest) GetBlockTime() time.Time { + if m != nil { + return m.BlockTime + } + return time.Time{} +} + +func (m *QueryTraceTxRequest) GetProposerAddress() github_com_cosmos_cosmos_sdk_types.ConsAddress { + if m != nil { + return m.ProposerAddress + } + return nil +} + +func (m *QueryTraceTxRequest) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *QueryTraceTxRequest) GetBlockMaxGas() int64 { + if m != nil { + return m.BlockMaxGas + } + return 0 +} + +// QueryTraceTxResponse defines TraceTx response +type QueryTraceTxResponse struct { + // data is the response serialized in bytes + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *QueryTraceTxResponse) Reset() { *m = QueryTraceTxResponse{} } +func (m *QueryTraceTxResponse) String() string { return proto.CompactTextString(m) } +func (*QueryTraceTxResponse) ProtoMessage() {} +func (*QueryTraceTxResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{17} +} +func (m *QueryTraceTxResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTraceTxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTraceTxResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryTraceTxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTraceTxResponse.Merge(m, src) +} +func (m *QueryTraceTxResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTraceTxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTraceTxResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTraceTxResponse proto.InternalMessageInfo + +func (m *QueryTraceTxResponse) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +// QueryTraceBlockRequest defines TraceTx request +type QueryTraceBlockRequest struct { + // txs is an array of messages in the block + Txs []*MsgEthereumTx `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"` + // trace_config holds extra parameters to trace functions. + TraceConfig *TraceConfig `protobuf:"bytes,3,opt,name=trace_config,json=traceConfig,proto3" json:"trace_config,omitempty"` + // block_number of the traced block + BlockNumber int64 `protobuf:"varint,5,opt,name=block_number,json=blockNumber,proto3" json:"block_number,omitempty"` + // block_hash (hex) of the traced block + BlockHash string `protobuf:"bytes,6,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` + // block_time of the traced block + BlockTime time.Time `protobuf:"bytes,7,opt,name=block_time,json=blockTime,proto3,stdtime" json:"block_time"` + // proposer_address is the address of the requested block + ProposerAddress github_com_cosmos_cosmos_sdk_types.ConsAddress `protobuf:"bytes,8,opt,name=proposer_address,json=proposerAddress,proto3,casttype=github.com/cosmos/cosmos-sdk/types.ConsAddress" json:"proposer_address,omitempty"` + // chain_id is the eip155 chain id parsed from the requested block header + ChainId int64 `protobuf:"varint,9,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // block_max_gas of the traced block + BlockMaxGas int64 `protobuf:"varint,10,opt,name=block_max_gas,json=blockMaxGas,proto3" json:"block_max_gas,omitempty"` +} + +func (m *QueryTraceBlockRequest) Reset() { *m = QueryTraceBlockRequest{} } +func (m *QueryTraceBlockRequest) String() string { return proto.CompactTextString(m) } +func (*QueryTraceBlockRequest) ProtoMessage() {} +func (*QueryTraceBlockRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{18} +} +func (m *QueryTraceBlockRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTraceBlockRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTraceBlockRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryTraceBlockRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTraceBlockRequest.Merge(m, src) +} +func (m *QueryTraceBlockRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryTraceBlockRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTraceBlockRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTraceBlockRequest proto.InternalMessageInfo + +func (m *QueryTraceBlockRequest) GetTxs() []*MsgEthereumTx { + if m != nil { + return m.Txs + } + return nil +} + +func (m *QueryTraceBlockRequest) GetTraceConfig() *TraceConfig { + if m != nil { + return m.TraceConfig + } + return nil +} + +func (m *QueryTraceBlockRequest) GetBlockNumber() int64 { + if m != nil { + return m.BlockNumber + } + return 0 +} + +func (m *QueryTraceBlockRequest) GetBlockHash() string { + if m != nil { + return m.BlockHash + } + return "" +} + +func (m *QueryTraceBlockRequest) GetBlockTime() time.Time { + if m != nil { + return m.BlockTime + } + return time.Time{} +} + +func (m *QueryTraceBlockRequest) GetProposerAddress() github_com_cosmos_cosmos_sdk_types.ConsAddress { + if m != nil { + return m.ProposerAddress + } + return nil +} + +func (m *QueryTraceBlockRequest) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *QueryTraceBlockRequest) GetBlockMaxGas() int64 { + if m != nil { + return m.BlockMaxGas + } + return 0 +} + +// QueryTraceBlockResponse defines TraceBlock response +type QueryTraceBlockResponse struct { + // data is the response serialized in bytes + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *QueryTraceBlockResponse) Reset() { *m = QueryTraceBlockResponse{} } +func (m *QueryTraceBlockResponse) String() string { return proto.CompactTextString(m) } +func (*QueryTraceBlockResponse) ProtoMessage() {} +func (*QueryTraceBlockResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{19} +} +func (m *QueryTraceBlockResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryTraceBlockResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryTraceBlockResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryTraceBlockResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryTraceBlockResponse.Merge(m, src) +} +func (m *QueryTraceBlockResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryTraceBlockResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryTraceBlockResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryTraceBlockResponse proto.InternalMessageInfo + +func (m *QueryTraceBlockResponse) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +// QueryBaseFeeRequest defines the request type for querying the EIP1559 base +// fee. +type QueryBaseFeeRequest struct { +} + +func (m *QueryBaseFeeRequest) Reset() { *m = QueryBaseFeeRequest{} } +func (m *QueryBaseFeeRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBaseFeeRequest) ProtoMessage() {} +func (*QueryBaseFeeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{20} +} +func (m *QueryBaseFeeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBaseFeeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBaseFeeRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryBaseFeeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBaseFeeRequest.Merge(m, src) +} +func (m *QueryBaseFeeRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBaseFeeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBaseFeeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBaseFeeRequest proto.InternalMessageInfo + +// QueryBaseFeeResponse returns the EIP1559 base fee. +// See https://github.com/ethereum/EIPs/blob/ba6c342c23164072adb500c3136e3ae6eabff306/EIPS/eip-1559.md. +type QueryBaseFeeResponse struct { + // base_fee is the EIP1559 base fee in units of wei. + BaseFee *cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=base_fee,json=baseFee,proto3,customtype=cosmossdk.io/math.Int" json:"base_fee,omitempty"` + // base_fee is the EIP1559 base fee in units of micronibi ("unibi"). + BaseFeeUnibi *cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=base_fee_unibi,json=baseFeeUnibi,proto3,customtype=cosmossdk.io/math.Int" json:"base_fee_unibi,omitempty"` +} + +func (m *QueryBaseFeeResponse) Reset() { *m = QueryBaseFeeResponse{} } +func (m *QueryBaseFeeResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBaseFeeResponse) ProtoMessage() {} +func (*QueryBaseFeeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{21} +} +func (m *QueryBaseFeeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBaseFeeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBaseFeeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryBaseFeeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBaseFeeResponse.Merge(m, src) +} +func (m *QueryBaseFeeResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBaseFeeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBaseFeeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBaseFeeResponse proto.InternalMessageInfo + +type QueryFunTokenMappingRequest struct { + // Either the hexadecimal-encoded ERC20 contract address or denomination of the + // Bank Coin. + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` +} + +func (m *QueryFunTokenMappingRequest) Reset() { *m = QueryFunTokenMappingRequest{} } +func (m *QueryFunTokenMappingRequest) String() string { return proto.CompactTextString(m) } +func (*QueryFunTokenMappingRequest) ProtoMessage() {} +func (*QueryFunTokenMappingRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{22} +} +func (m *QueryFunTokenMappingRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFunTokenMappingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFunTokenMappingRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryFunTokenMappingRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFunTokenMappingRequest.Merge(m, src) +} +func (m *QueryFunTokenMappingRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryFunTokenMappingRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFunTokenMappingRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFunTokenMappingRequest proto.InternalMessageInfo + +type QueryFunTokenMappingResponse struct { + // fun_token is a mapping between the Bank Coin and the ERC20 contract address + FunToken *FunToken `protobuf:"bytes,1,opt,name=fun_token,json=funToken,proto3" json:"fun_token,omitempty"` +} + +func (m *QueryFunTokenMappingResponse) Reset() { *m = QueryFunTokenMappingResponse{} } +func (m *QueryFunTokenMappingResponse) String() string { return proto.CompactTextString(m) } +func (*QueryFunTokenMappingResponse) ProtoMessage() {} +func (*QueryFunTokenMappingResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ffa36cdc5add14ed, []int{23} +} +func (m *QueryFunTokenMappingResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryFunTokenMappingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryFunTokenMappingResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryFunTokenMappingResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryFunTokenMappingResponse.Merge(m, src) +} +func (m *QueryFunTokenMappingResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryFunTokenMappingResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryFunTokenMappingResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryFunTokenMappingResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*QueryEthAccountRequest)(nil), "eth.evm.v1.QueryEthAccountRequest") + proto.RegisterType((*QueryEthAccountResponse)(nil), "eth.evm.v1.QueryEthAccountResponse") + proto.RegisterType((*QueryValidatorAccountRequest)(nil), "eth.evm.v1.QueryValidatorAccountRequest") + proto.RegisterType((*QueryValidatorAccountResponse)(nil), "eth.evm.v1.QueryValidatorAccountResponse") + proto.RegisterType((*QueryBalanceRequest)(nil), "eth.evm.v1.QueryBalanceRequest") + proto.RegisterType((*QueryBalanceResponse)(nil), "eth.evm.v1.QueryBalanceResponse") + proto.RegisterType((*QueryStorageRequest)(nil), "eth.evm.v1.QueryStorageRequest") + proto.RegisterType((*QueryStorageResponse)(nil), "eth.evm.v1.QueryStorageResponse") + proto.RegisterType((*QueryCodeRequest)(nil), "eth.evm.v1.QueryCodeRequest") + proto.RegisterType((*QueryCodeResponse)(nil), "eth.evm.v1.QueryCodeResponse") + proto.RegisterType((*QueryTxLogsRequest)(nil), "eth.evm.v1.QueryTxLogsRequest") + proto.RegisterType((*QueryTxLogsResponse)(nil), "eth.evm.v1.QueryTxLogsResponse") + proto.RegisterType((*QueryParamsRequest)(nil), "eth.evm.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "eth.evm.v1.QueryParamsResponse") + proto.RegisterType((*EthCallRequest)(nil), "eth.evm.v1.EthCallRequest") + proto.RegisterType((*EstimateGasResponse)(nil), "eth.evm.v1.EstimateGasResponse") + proto.RegisterType((*QueryTraceTxRequest)(nil), "eth.evm.v1.QueryTraceTxRequest") + proto.RegisterType((*QueryTraceTxResponse)(nil), "eth.evm.v1.QueryTraceTxResponse") + proto.RegisterType((*QueryTraceBlockRequest)(nil), "eth.evm.v1.QueryTraceBlockRequest") + proto.RegisterType((*QueryTraceBlockResponse)(nil), "eth.evm.v1.QueryTraceBlockResponse") + proto.RegisterType((*QueryBaseFeeRequest)(nil), "eth.evm.v1.QueryBaseFeeRequest") + proto.RegisterType((*QueryBaseFeeResponse)(nil), "eth.evm.v1.QueryBaseFeeResponse") + proto.RegisterType((*QueryFunTokenMappingRequest)(nil), "eth.evm.v1.QueryFunTokenMappingRequest") + proto.RegisterType((*QueryFunTokenMappingResponse)(nil), "eth.evm.v1.QueryFunTokenMappingResponse") +} + +func init() { proto.RegisterFile("eth/evm/v1/query.proto", fileDescriptor_ffa36cdc5add14ed) } + +var fileDescriptor_ffa36cdc5add14ed = []byte{ + // 1592 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x56, 0x4f, 0x6f, 0x5b, 0xc5, + 0x16, 0x8f, 0x63, 0x27, 0x76, 0x8e, 0xd3, 0x24, 0x6f, 0x9a, 0x36, 0x89, 0x93, 0xd8, 0xc9, 0xcd, + 0x7b, 0x49, 0xda, 0xd7, 0xde, 0xfb, 0xe2, 0x3e, 0xbd, 0xa7, 0x57, 0xbd, 0x0a, 0xea, 0x28, 0x0d, + 0xa5, 0x7f, 0xd4, 0x9a, 0x00, 0x12, 0x08, 0x59, 0x63, 0x7b, 0x72, 0x7d, 0x15, 0xfb, 0x8e, 0xeb, + 0x19, 0xbb, 0x0e, 0x25, 0x1b, 0xba, 0x41, 0x42, 0x95, 0x2a, 0xf1, 0x05, 0xba, 0xe2, 0x23, 0xc0, + 0x57, 0xe8, 0x8e, 0x4a, 0x6c, 0x10, 0x8b, 0x82, 0x5a, 0x16, 0xac, 0x59, 0xb2, 0x42, 0xf3, 0xcf, + 0xbe, 0xb6, 0xaf, 0x13, 0xaa, 0xc2, 0x8e, 0xd5, 0x9d, 0x39, 0x73, 0xfe, 0xfc, 0xe6, 0xcc, 0xb9, + 0xe7, 0xfc, 0xe0, 0x2c, 0xe1, 0x15, 0x87, 0xb4, 0x6a, 0x4e, 0x6b, 0xcb, 0xb9, 0xd7, 0x24, 0x8d, + 0x43, 0xbb, 0xde, 0xa0, 0x9c, 0x22, 0x20, 0xbc, 0x62, 0x93, 0x56, 0xcd, 0x6e, 0x6d, 0xa5, 0xce, + 0x97, 0x28, 0xab, 0x51, 0xe6, 0x14, 0x31, 0x23, 0x4a, 0xc9, 0x69, 0x6d, 0x15, 0x09, 0xc7, 0x5b, + 0x4e, 0x1d, 0xbb, 0x9e, 0x8f, 0xb9, 0x47, 0x7d, 0x65, 0x97, 0x9a, 0x0d, 0xf8, 0x13, 0xe6, 0x4a, + 0x7a, 0x3a, 0x20, 0xe5, 0x6d, 0xa3, 0xea, 0x52, 0x97, 0xca, 0xa5, 0x23, 0x56, 0x5a, 0xba, 0xe4, + 0x52, 0xea, 0x56, 0x89, 0x83, 0xeb, 0x9e, 0x83, 0x7d, 0x9f, 0x72, 0xe9, 0x9d, 0xe9, 0xd3, 0x8c, + 0x3e, 0x95, 0xbb, 0x62, 0x73, 0xdf, 0xe1, 0x5e, 0x8d, 0x30, 0x8e, 0x6b, 0x75, 0xa5, 0x60, 0xfd, + 0x1f, 0xce, 0xde, 0x15, 0x08, 0x77, 0x78, 0xe5, 0x6a, 0xa9, 0x44, 0x9b, 0x3e, 0xcf, 0x93, 0x7b, + 0x4d, 0xc2, 0x38, 0x9a, 0x87, 0x38, 0x2e, 0x97, 0x1b, 0x84, 0xb1, 0xf9, 0xc8, 0x4a, 0x64, 0x73, + 0x22, 0x6f, 0xb6, 0x97, 0x13, 0x9f, 0x3d, 0xc9, 0x8c, 0xfc, 0xfc, 0x24, 0x33, 0x62, 0x7d, 0x13, + 0x81, 0xb9, 0x01, 0x73, 0x56, 0xa7, 0x3e, 0x23, 0xc2, 0xbe, 0x88, 0xab, 0xd8, 0x2f, 0x11, 0x63, + 0xaf, 0xb7, 0x28, 0x03, 0x49, 0xbd, 0x2c, 0xdc, 0x27, 0xde, 0xfc, 0xa8, 0x3c, 0x05, 0x2d, 0x7a, + 0x9f, 0x78, 0x68, 0x11, 0x26, 0x4a, 0xb4, 0x4c, 0x0a, 0x15, 0xcc, 0x2a, 0xf3, 0x51, 0x79, 0x9c, + 0x10, 0x82, 0xb7, 0x30, 0xab, 0xa0, 0x59, 0x18, 0xf3, 0xa9, 0xf0, 0x1a, 0x5b, 0x89, 0x6c, 0xc6, + 0xf2, 0x6a, 0x23, 0x7c, 0x12, 0x5e, 0x29, 0x18, 0xc4, 0x63, 0xca, 0x27, 0xe1, 0x95, 0xab, 0x4a, + 0x82, 0xfe, 0x01, 0x53, 0x45, 0x52, 0xaa, 0x5c, 0xca, 0x76, 0x74, 0xc6, 0xa5, 0xce, 0x29, 0x25, + 0xd5, 0x6a, 0xd6, 0x0d, 0x58, 0x92, 0x17, 0x7a, 0x0f, 0x57, 0xbd, 0x32, 0xe6, 0xb4, 0xd1, 0x97, + 0x95, 0x55, 0x98, 0x2c, 0x51, 0x9f, 0x15, 0x7a, 0x53, 0x93, 0x14, 0xb2, 0xab, 0x03, 0xe9, 0xf9, + 0x3c, 0x02, 0xcb, 0x43, 0xbc, 0xe9, 0x24, 0x6d, 0xc0, 0x34, 0x56, 0xa2, 0x3e, 0x8f, 0x53, 0x5a, + 0x6c, 0xe0, 0xa7, 0x20, 0xc1, 0x04, 0x04, 0x71, 0xf1, 0x51, 0x79, 0xf1, 0xce, 0x5e, 0x5c, 0xcd, + 0x38, 0xf1, 0x9b, 0xb5, 0x22, 0x69, 0xc8, 0x9c, 0xc5, 0xf2, 0xa7, 0xb4, 0xf4, 0xb6, 0x14, 0x5a, + 0xff, 0x83, 0xd3, 0x12, 0x4c, 0x4e, 0x25, 0xfa, 0x55, 0xde, 0xf9, 0x2e, 0xcc, 0xf6, 0x9a, 0xbe, + 0xf6, 0x1b, 0x5b, 0x37, 0x34, 0x9a, 0x77, 0x38, 0x6d, 0x60, 0xf7, 0x64, 0x34, 0x68, 0x06, 0xa2, + 0x07, 0xe4, 0x50, 0x7b, 0x12, 0xcb, 0x00, 0xbe, 0x0b, 0x1a, 0x5f, 0xc7, 0x99, 0xc6, 0x37, 0x0b, + 0x63, 0x2d, 0x5c, 0x6d, 0x1a, 0x74, 0x6a, 0x63, 0xfd, 0x07, 0x66, 0xa4, 0xf6, 0x36, 0x2d, 0xbf, + 0x52, 0x16, 0x36, 0xe0, 0x6f, 0x01, 0x3b, 0x1d, 0x02, 0x41, 0x4c, 0x94, 0xa6, 0xb4, 0x9a, 0xcc, + 0xcb, 0xb5, 0xf5, 0x31, 0x20, 0xa9, 0xb8, 0xd7, 0xbe, 0x49, 0x5d, 0x66, 0x42, 0x20, 0x88, 0xc9, + 0x82, 0x56, 0xfe, 0xe5, 0x1a, 0x5d, 0x03, 0xe8, 0xb6, 0x04, 0x79, 0xb7, 0x64, 0x76, 0xdd, 0x56, + 0xfd, 0xc3, 0x16, 0xfd, 0xc3, 0x56, 0x4d, 0x46, 0xf7, 0x0f, 0xfb, 0x4e, 0x37, 0x55, 0xf9, 0x80, + 0x65, 0x00, 0xe4, 0xc3, 0x88, 0x4e, 0xac, 0x09, 0xae, 0x71, 0xae, 0x41, 0xac, 0x4a, 0x5d, 0x71, + 0xbb, 0xe8, 0x66, 0x32, 0x3b, 0x6d, 0x77, 0xfb, 0x95, 0x7d, 0x93, 0xba, 0x79, 0x79, 0x88, 0x76, + 0x43, 0xe0, 0x6c, 0x9c, 0x08, 0x47, 0x45, 0x08, 0xe2, 0xb1, 0x66, 0x75, 0x06, 0xee, 0xe0, 0x06, + 0xae, 0x99, 0x0c, 0x58, 0xbb, 0x1a, 0x9a, 0x91, 0x6a, 0x68, 0xff, 0x82, 0xf1, 0xba, 0x94, 0xc8, + 0xd4, 0x24, 0xb3, 0x28, 0x08, 0x4e, 0xe9, 0xe6, 0x62, 0x4f, 0x9f, 0x67, 0x46, 0xf2, 0x5a, 0xcf, + 0xfa, 0x3a, 0x02, 0x53, 0x3b, 0xbc, 0xb2, 0x8d, 0xab, 0xd5, 0x40, 0x76, 0x71, 0xc3, 0x65, 0xe6, + 0x1d, 0xc4, 0x1a, 0xcd, 0x41, 0xdc, 0xc5, 0xac, 0x50, 0xc2, 0x75, 0xfd, 0xcf, 0x8c, 0xbb, 0x98, + 0x6d, 0xe3, 0x3a, 0xfa, 0x08, 0x66, 0xea, 0x0d, 0x5a, 0xa7, 0x8c, 0x34, 0x3a, 0xff, 0x9d, 0xf8, + 0x67, 0x26, 0x73, 0xd9, 0x5f, 0x9f, 0x67, 0x6c, 0xd7, 0xe3, 0x95, 0x66, 0xd1, 0x2e, 0xd1, 0x9a, + 0xa3, 0x5b, 0xb9, 0xfa, 0x5c, 0x64, 0xe5, 0x03, 0x87, 0x1f, 0xd6, 0x09, 0xb3, 0xb7, 0xbb, 0x3f, + 0x7c, 0x7e, 0xda, 0xf8, 0x32, 0x3f, 0xeb, 0x02, 0x24, 0x4a, 0x15, 0xec, 0xf9, 0x05, 0xaf, 0x2c, + 0xbb, 0x54, 0x34, 0x1f, 0x97, 0xfb, 0xeb, 0x65, 0x6b, 0x03, 0x4e, 0xef, 0x30, 0xee, 0xd5, 0x30, + 0x27, 0xbb, 0xb8, 0x9b, 0x82, 0x19, 0x88, 0xba, 0x58, 0x81, 0x8f, 0xe5, 0xc5, 0xd2, 0xfa, 0x25, + 0x6a, 0xde, 0xb1, 0x81, 0x4b, 0x64, 0xaf, 0x6d, 0xee, 0xf9, 0x4f, 0x88, 0xd6, 0x98, 0xab, 0x33, + 0xb5, 0x10, 0xcc, 0xd4, 0x2d, 0xe6, 0xee, 0xf0, 0x0a, 0x69, 0x90, 0x66, 0x6d, 0xaf, 0x9d, 0x17, + 0x5a, 0xe8, 0x32, 0x4c, 0x72, 0x61, 0x5e, 0x28, 0x51, 0x7f, 0xdf, 0x73, 0xe5, 0x1d, 0x93, 0xd9, + 0xb9, 0xa0, 0x95, 0x74, 0xbf, 0x2d, 0x8f, 0xf3, 0x49, 0xde, 0xdd, 0xa0, 0x2b, 0x30, 0x59, 0x6f, + 0x90, 0x32, 0x29, 0x11, 0xc6, 0x68, 0x83, 0xcd, 0xc7, 0x64, 0xe1, 0x1c, 0x13, 0xb1, 0x47, 0x5d, + 0x34, 0xca, 0x62, 0x95, 0x96, 0x0e, 0x4c, 0x4b, 0x1a, 0x93, 0x79, 0x48, 0x4a, 0x99, 0x6a, 0x48, + 0x68, 0x19, 0x40, 0xa9, 0xc8, 0xdf, 0x42, 0xb5, 0xe3, 0x09, 0x29, 0x91, 0x8d, 0x7e, 0xdb, 0x1c, + 0x8b, 0x99, 0x35, 0x1f, 0x97, 0xd0, 0x53, 0xb6, 0x1a, 0x68, 0xb6, 0x19, 0x68, 0xf6, 0x9e, 0x19, + 0x68, 0xb9, 0x84, 0x28, 0x91, 0xc7, 0x3f, 0x64, 0x22, 0xda, 0x89, 0x38, 0x09, 0x7d, 0xe9, 0xc4, + 0x9f, 0xf3, 0xd2, 0x13, 0x3d, 0x2f, 0x8d, 0x2c, 0x38, 0xa5, 0xe0, 0xd7, 0x70, 0xbb, 0x20, 0x1e, + 0x17, 0x02, 0x19, 0xb8, 0x85, 0xdb, 0xbb, 0x98, 0xbd, 0x1d, 0x4b, 0x8c, 0xce, 0x44, 0xf3, 0x09, + 0xde, 0x2e, 0x78, 0x7e, 0x99, 0xb4, 0xad, 0xf3, 0xba, 0x8f, 0x75, 0xde, 0xbc, 0xdb, 0x64, 0xca, + 0x98, 0x63, 0x53, 0xdc, 0x62, 0x6d, 0x7d, 0x19, 0xd5, 0xa3, 0x5b, 0x2a, 0xe7, 0x84, 0xd7, 0x40, + 0x8d, 0xf0, 0xb6, 0xf9, 0xd5, 0x8f, 0xab, 0x11, 0xde, 0x66, 0xaf, 0x55, 0x23, 0x7f, 0x3d, 0xf2, + 0xc9, 0x8f, 0x6c, 0x5d, 0xd4, 0x1c, 0x29, 0xf8, 0x4e, 0xc7, 0xbc, 0xeb, 0x99, 0xce, 0x98, 0x66, + 0xe4, 0x1a, 0x31, 0xdd, 0xde, 0x7a, 0x14, 0xe9, 0xcc, 0x60, 0x2d, 0xd7, 0x3e, 0xfe, 0x0d, 0x09, + 0xd1, 0x99, 0x0b, 0xfb, 0x44, 0x8f, 0xb9, 0xdc, 0xc2, 0xf7, 0xcf, 0x33, 0x67, 0xd4, 0x15, 0x59, + 0xf9, 0xc0, 0xf6, 0xa8, 0x53, 0xc3, 0xbc, 0x62, 0x5f, 0xf7, 0xb9, 0x98, 0xcf, 0xd2, 0x1a, 0xbd, + 0x01, 0x53, 0xc6, 0xaa, 0xd0, 0xf4, 0xbd, 0xa2, 0x1e, 0xd1, 0xc7, 0xd9, 0x4e, 0x6a, 0xdb, 0x77, + 0x85, 0xba, 0x75, 0x05, 0x16, 0x25, 0x9c, 0x6b, 0x4d, 0x7f, 0x8f, 0x1e, 0x10, 0xff, 0x16, 0xae, + 0xd7, 0x3d, 0xdf, 0x35, 0x25, 0x38, 0x0b, 0x63, 0x5c, 0x88, 0xcd, 0xe4, 0x95, 0x9b, 0xc0, 0x98, + 0xfa, 0x50, 0xf3, 0xac, 0x01, 0x73, 0x7d, 0xab, 0x2d, 0x98, 0xd8, 0x6f, 0xfa, 0x85, 0xae, 0x8f, + 0x64, 0x76, 0x36, 0x58, 0x92, 0xc6, 0x2e, 0x9f, 0xd8, 0xd7, 0xab, 0xae, 0xf3, 0xec, 0x57, 0x93, + 0x30, 0x26, 0xbd, 0xa3, 0x87, 0x11, 0x80, 0x2e, 0x37, 0x45, 0x56, 0xd0, 0x45, 0x38, 0xef, 0x4d, + 0xad, 0x1d, 0xab, 0xa3, 0xe0, 0x59, 0x17, 0x3e, 0xfd, 0xf6, 0xa7, 0x2f, 0x46, 0xd7, 0xd1, 0xdf, + 0x1d, 0x91, 0x8c, 0x46, 0xb3, 0x43, 0xe1, 0x05, 0x07, 0x55, 0xba, 0xce, 0x03, 0x5d, 0x8a, 0x47, + 0xe8, 0x49, 0x04, 0x66, 0xfa, 0x29, 0x20, 0xda, 0x1c, 0x88, 0x33, 0x84, 0x73, 0xa6, 0xce, 0xfd, + 0x0e, 0x4d, 0x8d, 0xeb, 0xbf, 0x12, 0xd7, 0x16, 0x72, 0xfa, 0x70, 0xb5, 0x8c, 0x41, 0x17, 0x5d, + 0x90, 0xc6, 0x1e, 0xa1, 0xfb, 0x10, 0xcf, 0x19, 0xea, 0x36, 0x10, 0xae, 0x97, 0x31, 0xa6, 0x56, + 0x86, 0x2b, 0x68, 0x18, 0xe7, 0x24, 0x8c, 0x35, 0xb4, 0xda, 0x07, 0x43, 0xf3, 0x3f, 0x16, 0xc8, + 0xcd, 0x27, 0x10, 0xd7, 0xac, 0x2d, 0x24, 0x70, 0x2f, 0x39, 0x0c, 0x09, 0xdc, 0x47, 0xf8, 0x2c, + 0x5b, 0x06, 0xde, 0x44, 0xeb, 0x7d, 0x81, 0x99, 0xd2, 0xeb, 0xc6, 0x75, 0x1e, 0x1c, 0x90, 0xc3, + 0x23, 0x74, 0x00, 0x31, 0xc1, 0xe6, 0xd0, 0xd2, 0x80, 0xe7, 0x00, 0x39, 0x4c, 0x2d, 0x0f, 0x39, + 0xd5, 0x41, 0xd7, 0x65, 0xd0, 0x15, 0x94, 0xee, 0x0b, 0x2a, 0xb8, 0x60, 0xf0, 0xaa, 0x15, 0x18, + 0x57, 0x6c, 0x06, 0xa5, 0x07, 0x1c, 0xf6, 0x10, 0xa5, 0x54, 0x66, 0xe8, 0xb9, 0x0e, 0xb9, 0x2c, + 0x43, 0xce, 0xa1, 0x33, 0x7d, 0x21, 0x15, 0x3f, 0x42, 0x1e, 0xc4, 0x35, 0x3d, 0x42, 0xa9, 0xa0, + 0xab, 0x5e, 0xce, 0x94, 0x5a, 0x1d, 0x3e, 0x1a, 0x4c, 0xa0, 0x8c, 0x0c, 0xb4, 0x80, 0xe6, 0x42, + 0x0a, 0xbd, 0x24, 0xfc, 0x53, 0x48, 0x06, 0x08, 0xcd, 0xb1, 0xe1, 0x7a, 0x6e, 0x15, 0xc2, 0x82, + 0xac, 0x35, 0x19, 0x6c, 0x19, 0x2d, 0xf6, 0x07, 0xd3, 0xba, 0xa2, 0xc3, 0xa2, 0x1a, 0xc4, 0xf5, + 0x78, 0x0c, 0x29, 0x98, 0x5e, 0xb2, 0x14, 0x52, 0x30, 0x7d, 0x93, 0x75, 0xe8, 0xfd, 0xd4, 0x48, + 0xe4, 0x6d, 0x74, 0x08, 0xd0, 0x6d, 0xdc, 0x21, 0x0d, 0x64, 0x60, 0xfa, 0x86, 0x34, 0x90, 0xc1, + 0xce, 0x6f, 0x59, 0x32, 0xee, 0x12, 0x4a, 0x85, 0xc6, 0x95, 0xe3, 0x03, 0xdd, 0x83, 0x09, 0x35, + 0x79, 0x45, 0x9e, 0xff, 0x80, 0xbb, 0xae, 0xca, 0x98, 0x8b, 0x68, 0x21, 0x34, 0xa6, 0x7c, 0xcd, + 0x9a, 0x68, 0x03, 0x6a, 0x42, 0x84, 0xb5, 0x81, 0xe0, 0x44, 0x0a, 0x6d, 0x03, 0x3d, 0xa3, 0x69, + 0x68, 0x72, 0xcd, 0xe4, 0x41, 0x8f, 0x22, 0x30, 0xdd, 0x37, 0x01, 0xd0, 0xc6, 0x80, 0xdb, 0xf0, + 0x11, 0x93, 0xda, 0x3c, 0x59, 0x51, 0xe3, 0xd8, 0x90, 0x38, 0x56, 0x51, 0xa6, 0x0f, 0xc7, 0x7e, + 0xd3, 0x97, 0x03, 0xc6, 0x79, 0x20, 0x3f, 0x47, 0xb9, 0x37, 0x9f, 0xbe, 0x48, 0x47, 0x9e, 0xbd, + 0x48, 0x47, 0x7e, 0x7c, 0x91, 0x8e, 0x3c, 0x7e, 0x99, 0x1e, 0x79, 0xf6, 0x32, 0x3d, 0xf2, 0xdd, + 0xcb, 0xf4, 0xc8, 0x07, 0xeb, 0x01, 0x12, 0x71, 0x5b, 0x3a, 0xd9, 0x16, 0x14, 0xc0, 0x38, 0x6c, + 0x65, 0x9d, 0xb6, 0xf0, 0x5a, 0x1c, 0x97, 0x9c, 0xe5, 0xd2, 0x6f, 0x01, 0x00, 0x00, 0xff, 0xff, + 0xe6, 0x31, 0x27, 0xb5, 0x28, 0x12, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // EthAccount queries a Nibiru account using its EVM address or Bech32 Nibiru + // address. + EthAccount(ctx context.Context, in *QueryEthAccountRequest, opts ...grpc.CallOption) (*QueryEthAccountResponse, error) + // ValidatorAccount queries an Ethereum account's from a validator consensus + // Address. + ValidatorAccount(ctx context.Context, in *QueryValidatorAccountRequest, opts ...grpc.CallOption) (*QueryValidatorAccountResponse, error) + // Balance queries the balance of a the EVM denomination for a single + // EthAccount. + Balance(ctx context.Context, in *QueryBalanceRequest, opts ...grpc.CallOption) (*QueryBalanceResponse, error) + // Storage queries the balance of all coins for a single account. + Storage(ctx context.Context, in *QueryStorageRequest, opts ...grpc.CallOption) (*QueryStorageResponse, error) + // Code queries the balance of all coins for a single account. + Code(ctx context.Context, in *QueryCodeRequest, opts ...grpc.CallOption) (*QueryCodeResponse, error) + // Params queries the parameters of x/evm module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // EthCall implements the `eth_call` rpc api + EthCall(ctx context.Context, in *EthCallRequest, opts ...grpc.CallOption) (*MsgEthereumTxResponse, error) + // EstimateGas implements the `eth_estimateGas` rpc api + EstimateGas(ctx context.Context, in *EthCallRequest, opts ...grpc.CallOption) (*EstimateGasResponse, error) + // TraceTx implements the `debug_traceTransaction` rpc api + TraceTx(ctx context.Context, in *QueryTraceTxRequest, opts ...grpc.CallOption) (*QueryTraceTxResponse, error) + // TraceBlock implements the `debug_traceBlockByNumber` and `debug_traceBlockByHash` rpc api + TraceBlock(ctx context.Context, in *QueryTraceBlockRequest, opts ...grpc.CallOption) (*QueryTraceBlockResponse, error) + // TraceCall implements the `debug_traceCall` rpc api + TraceCall(ctx context.Context, in *QueryTraceTxRequest, opts ...grpc.CallOption) (*QueryTraceTxResponse, error) + // BaseFee queries the base fee of the parent block of the current block, + // Similar to feemarket module's method + BaseFee(ctx context.Context, in *QueryBaseFeeRequest, opts ...grpc.CallOption) (*QueryBaseFeeResponse, error) + FunTokenMapping(ctx context.Context, in *QueryFunTokenMappingRequest, opts ...grpc.CallOption) (*QueryFunTokenMappingResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) EthAccount(ctx context.Context, in *QueryEthAccountRequest, opts ...grpc.CallOption) (*QueryEthAccountResponse, error) { + out := new(QueryEthAccountResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/EthAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ValidatorAccount(ctx context.Context, in *QueryValidatorAccountRequest, opts ...grpc.CallOption) (*QueryValidatorAccountResponse, error) { + out := new(QueryValidatorAccountResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/ValidatorAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Balance(ctx context.Context, in *QueryBalanceRequest, opts ...grpc.CallOption) (*QueryBalanceResponse, error) { + out := new(QueryBalanceResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/Balance", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Storage(ctx context.Context, in *QueryStorageRequest, opts ...grpc.CallOption) (*QueryStorageResponse, error) { + out := new(QueryStorageResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/Storage", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Code(ctx context.Context, in *QueryCodeRequest, opts ...grpc.CallOption) (*QueryCodeResponse, error) { + out := new(QueryCodeResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/Code", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) EthCall(ctx context.Context, in *EthCallRequest, opts ...grpc.CallOption) (*MsgEthereumTxResponse, error) { + out := new(MsgEthereumTxResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/EthCall", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) EstimateGas(ctx context.Context, in *EthCallRequest, opts ...grpc.CallOption) (*EstimateGasResponse, error) { + out := new(EstimateGasResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/EstimateGas", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TraceTx(ctx context.Context, in *QueryTraceTxRequest, opts ...grpc.CallOption) (*QueryTraceTxResponse, error) { + out := new(QueryTraceTxResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/TraceTx", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TraceBlock(ctx context.Context, in *QueryTraceBlockRequest, opts ...grpc.CallOption) (*QueryTraceBlockResponse, error) { + out := new(QueryTraceBlockResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/TraceBlock", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) TraceCall(ctx context.Context, in *QueryTraceTxRequest, opts ...grpc.CallOption) (*QueryTraceTxResponse, error) { + out := new(QueryTraceTxResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/TraceCall", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BaseFee(ctx context.Context, in *QueryBaseFeeRequest, opts ...grpc.CallOption) (*QueryBaseFeeResponse, error) { + out := new(QueryBaseFeeResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/BaseFee", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) FunTokenMapping(ctx context.Context, in *QueryFunTokenMappingRequest, opts ...grpc.CallOption) (*QueryFunTokenMappingResponse, error) { + out := new(QueryFunTokenMappingResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Query/FunTokenMapping", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // EthAccount queries a Nibiru account using its EVM address or Bech32 Nibiru + // address. + EthAccount(context.Context, *QueryEthAccountRequest) (*QueryEthAccountResponse, error) + // ValidatorAccount queries an Ethereum account's from a validator consensus + // Address. + ValidatorAccount(context.Context, *QueryValidatorAccountRequest) (*QueryValidatorAccountResponse, error) + // Balance queries the balance of a the EVM denomination for a single + // EthAccount. + Balance(context.Context, *QueryBalanceRequest) (*QueryBalanceResponse, error) + // Storage queries the balance of all coins for a single account. + Storage(context.Context, *QueryStorageRequest) (*QueryStorageResponse, error) + // Code queries the balance of all coins for a single account. + Code(context.Context, *QueryCodeRequest) (*QueryCodeResponse, error) + // Params queries the parameters of x/evm module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // EthCall implements the `eth_call` rpc api + EthCall(context.Context, *EthCallRequest) (*MsgEthereumTxResponse, error) + // EstimateGas implements the `eth_estimateGas` rpc api + EstimateGas(context.Context, *EthCallRequest) (*EstimateGasResponse, error) + // TraceTx implements the `debug_traceTransaction` rpc api + TraceTx(context.Context, *QueryTraceTxRequest) (*QueryTraceTxResponse, error) + // TraceBlock implements the `debug_traceBlockByNumber` and `debug_traceBlockByHash` rpc api + TraceBlock(context.Context, *QueryTraceBlockRequest) (*QueryTraceBlockResponse, error) + // TraceCall implements the `debug_traceCall` rpc api + TraceCall(context.Context, *QueryTraceTxRequest) (*QueryTraceTxResponse, error) + // BaseFee queries the base fee of the parent block of the current block, + // Similar to feemarket module's method + BaseFee(context.Context, *QueryBaseFeeRequest) (*QueryBaseFeeResponse, error) + FunTokenMapping(context.Context, *QueryFunTokenMappingRequest) (*QueryFunTokenMappingResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) EthAccount(ctx context.Context, req *QueryEthAccountRequest) (*QueryEthAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EthAccount not implemented") +} +func (*UnimplementedQueryServer) ValidatorAccount(ctx context.Context, req *QueryValidatorAccountRequest) (*QueryValidatorAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidatorAccount not implemented") +} +func (*UnimplementedQueryServer) Balance(ctx context.Context, req *QueryBalanceRequest) (*QueryBalanceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Balance not implemented") +} +func (*UnimplementedQueryServer) Storage(ctx context.Context, req *QueryStorageRequest) (*QueryStorageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Storage not implemented") +} +func (*UnimplementedQueryServer) Code(ctx context.Context, req *QueryCodeRequest) (*QueryCodeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Code not implemented") +} +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) EthCall(ctx context.Context, req *EthCallRequest) (*MsgEthereumTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EthCall not implemented") +} +func (*UnimplementedQueryServer) EstimateGas(ctx context.Context, req *EthCallRequest) (*EstimateGasResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EstimateGas not implemented") +} +func (*UnimplementedQueryServer) TraceTx(ctx context.Context, req *QueryTraceTxRequest) (*QueryTraceTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TraceTx not implemented") +} +func (*UnimplementedQueryServer) TraceBlock(ctx context.Context, req *QueryTraceBlockRequest) (*QueryTraceBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TraceBlock not implemented") +} +func (*UnimplementedQueryServer) TraceCall(ctx context.Context, req *QueryTraceTxRequest) (*QueryTraceTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TraceCall not implemented") +} +func (*UnimplementedQueryServer) BaseFee(ctx context.Context, req *QueryBaseFeeRequest) (*QueryBaseFeeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BaseFee not implemented") +} +func (*UnimplementedQueryServer) FunTokenMapping(ctx context.Context, req *QueryFunTokenMappingRequest) (*QueryFunTokenMappingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FunTokenMapping not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_EthAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryEthAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).EthAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/EthAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).EthAccount(ctx, req.(*QueryEthAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ValidatorAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ValidatorAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/ValidatorAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ValidatorAccount(ctx, req.(*QueryValidatorAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Balance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBalanceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Balance(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/Balance", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Balance(ctx, req.(*QueryBalanceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Storage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryStorageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Storage(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/Storage", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Storage(ctx, req.(*QueryStorageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Code_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryCodeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Code(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/Code", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Code(ctx, req.(*QueryCodeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_EthCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EthCallRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).EthCall(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/EthCall", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).EthCall(ctx, req.(*EthCallRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_EstimateGas_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EthCallRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).EstimateGas(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/EstimateGas", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).EstimateGas(ctx, req.(*EthCallRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TraceTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTraceTxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TraceTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/TraceTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TraceTx(ctx, req.(*QueryTraceTxRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TraceBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTraceBlockRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TraceBlock(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/TraceBlock", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TraceBlock(ctx, req.(*QueryTraceBlockRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_TraceCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryTraceTxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).TraceCall(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/TraceCall", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).TraceCall(ctx, req.(*QueryTraceTxRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BaseFee_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBaseFeeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BaseFee(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/BaseFee", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BaseFee(ctx, req.(*QueryBaseFeeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_FunTokenMapping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryFunTokenMappingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).FunTokenMapping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Query/FunTokenMapping", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).FunTokenMapping(ctx, req.(*QueryFunTokenMappingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "eth.evm.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "EthAccount", + Handler: _Query_EthAccount_Handler, + }, + { + MethodName: "ValidatorAccount", + Handler: _Query_ValidatorAccount_Handler, + }, + { + MethodName: "Balance", + Handler: _Query_Balance_Handler, + }, + { + MethodName: "Storage", + Handler: _Query_Storage_Handler, + }, + { + MethodName: "Code", + Handler: _Query_Code_Handler, + }, + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "EthCall", + Handler: _Query_EthCall_Handler, + }, + { + MethodName: "EstimateGas", + Handler: _Query_EstimateGas_Handler, + }, + { + MethodName: "TraceTx", + Handler: _Query_TraceTx_Handler, + }, + { + MethodName: "TraceBlock", + Handler: _Query_TraceBlock_Handler, + }, + { + MethodName: "TraceCall", + Handler: _Query_TraceCall_Handler, + }, + { + MethodName: "BaseFee", + Handler: _Query_BaseFee_Handler, + }, + { + MethodName: "FunTokenMapping", + Handler: _Query_FunTokenMapping_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "eth/evm/v1/query.proto", +} + +func (m *QueryEthAccountRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryEthAccountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEthAccountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryEthAccountResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryEthAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEthAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Bech32Address) > 0 { + i -= len(m.Bech32Address) + copy(dAtA[i:], m.Bech32Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Bech32Address))) + i-- + dAtA[i] = 0x32 + } + if len(m.EthAddress) > 0 { + i -= len(m.EthAddress) + copy(dAtA[i:], m.EthAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.EthAddress))) + i-- + dAtA[i] = 0x2a + } + if m.Nonce != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x20 + } + if len(m.CodeHash) > 0 { + i -= len(m.CodeHash) + copy(dAtA[i:], m.CodeHash) + i = encodeVarintQuery(dAtA, i, uint64(len(m.CodeHash))) + i-- + dAtA[i] = 0x1a + } + if len(m.BalanceWei) > 0 { + i -= len(m.BalanceWei) + copy(dAtA[i:], m.BalanceWei) + i = encodeVarintQuery(dAtA, i, uint64(len(m.BalanceWei))) + i-- + dAtA[i] = 0x12 + } + if len(m.Balance) > 0 { + i -= len(m.Balance) + copy(dAtA[i:], m.Balance) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Balance))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorAccountRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidatorAccountRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorAccountRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ConsAddress) > 0 { + i -= len(m.ConsAddress) + copy(dAtA[i:], m.ConsAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ConsAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorAccountResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidatorAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AccountNumber != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.AccountNumber)) + i-- + dAtA[i] = 0x18 + } + if m.Sequence != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x10 + } + if len(m.AccountAddress) > 0 { + i -= len(m.AccountAddress) + copy(dAtA[i:], m.AccountAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.AccountAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBalanceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryBalanceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBalanceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBalanceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryBalanceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBalanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.BalanceWei) > 0 { + i -= len(m.BalanceWei) + copy(dAtA[i:], m.BalanceWei) + i = encodeVarintQuery(dAtA, i, uint64(len(m.BalanceWei))) + i-- + dAtA[i] = 0x12 + } + if len(m.Balance) > 0 { + i -= len(m.Balance) + copy(dAtA[i:], m.Balance) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Balance))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryStorageRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryStorageRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryStorageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryStorageResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryStorageResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryStorageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryCodeRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryCodeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCodeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Address) > 0 { + i -= len(m.Address) + copy(dAtA[i:], m.Address) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Address))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryCodeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryCodeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCodeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Code) > 0 { + i -= len(m.Code) + copy(dAtA[i:], m.Code) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Code))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryTxLogsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryTxLogsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTxLogsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryTxLogsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryTxLogsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTxLogsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Logs) > 0 { + for iNdEx := len(m.Logs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Logs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *EthCallRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EthCallRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EthCallRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ChainId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x20 + } + if len(m.ProposerAddress) > 0 { + i -= len(m.ProposerAddress) + copy(dAtA[i:], m.ProposerAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ProposerAddress))) + i-- + dAtA[i] = 0x1a + } + if m.GasCap != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.GasCap)) + i-- + dAtA[i] = 0x10 + } + if len(m.Args) > 0 { + i -= len(m.Args) + copy(dAtA[i:], m.Args) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Args))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EstimateGasResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EstimateGasResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EstimateGasResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Gas != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Gas)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryTraceTxRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryTraceTxRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTraceTxRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BlockMaxGas != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockMaxGas)) + i-- + dAtA[i] = 0x50 + } + if m.ChainId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x48 + } + if len(m.ProposerAddress) > 0 { + i -= len(m.ProposerAddress) + copy(dAtA[i:], m.ProposerAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ProposerAddress))) + i-- + dAtA[i] = 0x42 + } + n4, err4 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.BlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.BlockTime):]) + if err4 != nil { + return 0, err4 + } + i -= n4 + i = encodeVarintQuery(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x3a + if len(m.BlockHash) > 0 { + i -= len(m.BlockHash) + copy(dAtA[i:], m.BlockHash) + i = encodeVarintQuery(dAtA, i, uint64(len(m.BlockHash))) + i-- + dAtA[i] = 0x32 + } + if m.BlockNumber != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockNumber)) + i-- + dAtA[i] = 0x28 + } + if len(m.Predecessors) > 0 { + for iNdEx := len(m.Predecessors) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Predecessors[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.TraceConfig != nil { + { + size, err := m.TraceConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Msg != nil { + { + size, err := m.Msg.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryTraceTxResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryTraceTxResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTraceTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryTraceBlockRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryTraceBlockRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTraceBlockRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BlockMaxGas != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockMaxGas)) + i-- + dAtA[i] = 0x50 + } + if m.ChainId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x48 + } + if len(m.ProposerAddress) > 0 { + i -= len(m.ProposerAddress) + copy(dAtA[i:], m.ProposerAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ProposerAddress))) + i-- + dAtA[i] = 0x42 + } + n7, err7 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.BlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.BlockTime):]) + if err7 != nil { + return 0, err7 + } + i -= n7 + i = encodeVarintQuery(dAtA, i, uint64(n7)) + i-- + dAtA[i] = 0x3a + if len(m.BlockHash) > 0 { + i -= len(m.BlockHash) + copy(dAtA[i:], m.BlockHash) + i = encodeVarintQuery(dAtA, i, uint64(len(m.BlockHash))) + i-- + dAtA[i] = 0x32 + } + if m.BlockNumber != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockNumber)) + i-- + dAtA[i] = 0x28 + } + if m.TraceConfig != nil { + { + size, err := m.TraceConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Txs) > 0 { + for iNdEx := len(m.Txs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Txs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *QueryTraceBlockResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryTraceBlockResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryTraceBlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryBaseFeeRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryBaseFeeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBaseFeeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryBaseFeeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryBaseFeeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBaseFeeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BaseFeeUnibi != nil { + { + size := m.BaseFeeUnibi.Size() + i -= size + if _, err := m.BaseFeeUnibi.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.BaseFee != nil { + { + size := m.BaseFee.Size() + i -= size + if _, err := m.BaseFee.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFunTokenMappingRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryFunTokenMappingRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFunTokenMappingRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Token) > 0 { + i -= len(m.Token) + copy(dAtA[i:], m.Token) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Token))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryFunTokenMappingResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryFunTokenMappingResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryFunTokenMappingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.FunToken != nil { + { + size, err := m.FunToken.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryEthAccountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryEthAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Balance) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.BalanceWei) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.CodeHash) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Nonce != 0 { + n += 1 + sovQuery(uint64(m.Nonce)) + } + l = len(m.EthAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Bech32Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorAccountRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConsAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AccountAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovQuery(uint64(m.Sequence)) + } + if m.AccountNumber != 0 { + n += 1 + sovQuery(uint64(m.AccountNumber)) + } + return n +} + +func (m *QueryBalanceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBalanceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Balance) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.BalanceWei) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryStorageRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Key) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryStorageResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Value) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryCodeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Address) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryCodeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Code) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryTxLogsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryTxLogsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Logs) > 0 { + for _, e := range m.Logs { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *EthCallRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Args) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.GasCap != 0 { + n += 1 + sovQuery(uint64(m.GasCap)) + } + l = len(m.ProposerAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.ChainId != 0 { + n += 1 + sovQuery(uint64(m.ChainId)) + } + return n +} + +func (m *EstimateGasResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Gas != 0 { + n += 1 + sovQuery(uint64(m.Gas)) + } + return n +} + +func (m *QueryTraceTxRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Msg != nil { + l = m.Msg.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.TraceConfig != nil { + l = m.TraceConfig.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if len(m.Predecessors) > 0 { + for _, e := range m.Predecessors { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.BlockNumber != 0 { + n += 1 + sovQuery(uint64(m.BlockNumber)) + } + l = len(m.BlockHash) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.BlockTime) + n += 1 + l + sovQuery(uint64(l)) + l = len(m.ProposerAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.ChainId != 0 { + n += 1 + sovQuery(uint64(m.ChainId)) + } + if m.BlockMaxGas != 0 { + n += 1 + sovQuery(uint64(m.BlockMaxGas)) + } + return n +} + +func (m *QueryTraceTxResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryTraceBlockRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Txs) > 0 { + for _, e := range m.Txs { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.TraceConfig != nil { + l = m.TraceConfig.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.BlockNumber != 0 { + n += 1 + sovQuery(uint64(m.BlockNumber)) + } + l = len(m.BlockHash) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.BlockTime) + n += 1 + l + sovQuery(uint64(l)) + l = len(m.ProposerAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.ChainId != 0 { + n += 1 + sovQuery(uint64(m.ChainId)) + } + if m.BlockMaxGas != 0 { + n += 1 + sovQuery(uint64(m.BlockMaxGas)) + } + return n +} + +func (m *QueryTraceBlockResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryBaseFeeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryBaseFeeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BaseFee != nil { + l = m.BaseFee.Size() + n += 1 + l + sovQuery(uint64(l)) + } + if m.BaseFeeUnibi != nil { + l = m.BaseFeeUnibi.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFunTokenMappingRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Token) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryFunTokenMappingResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FunToken != nil { + l = m.FunToken.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryEthAccountRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryEthAccountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEthAccountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryEthAccountResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryEthAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEthAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Balance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Balance = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BalanceWei", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BalanceWei = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CodeHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CodeHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Nonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EthAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EthAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bech32Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bech32Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidatorAccountRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidatorAccountRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorAccountRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConsAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConsAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidatorAccountResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidatorAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccountAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountNumber", wireType) + } + m.AccountNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AccountNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryBalanceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryBalanceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBalanceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryBalanceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryBalanceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBalanceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Balance", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Balance = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BalanceWei", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BalanceWei = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryStorageRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryStorageRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryStorageRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryStorageResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryStorageResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryStorageResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCodeRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryCodeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCodeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCodeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryCodeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCodeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Code = append(m.Code[:0], dAtA[iNdEx:postIndex]...) + if m.Code == nil { + m.Code = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTxLogsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryTxLogsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTxLogsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTxLogsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryTxLogsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTxLogsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Logs = append(m.Logs, &Log{}) + if err := m.Logs[len(m.Logs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EthCallRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EthCallRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EthCallRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Args = append(m.Args[:0], dAtA[iNdEx:postIndex]...) + if m.Args == nil { + m.Args = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasCap", wireType) + } + m.GasCap = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasCap |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposerAddress", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProposerAddress = append(m.ProposerAddress[:0], dAtA[iNdEx:postIndex]...) + if m.ProposerAddress == nil { + m.ProposerAddress = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EstimateGasResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EstimateGasResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EstimateGasResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Gas", wireType) + } + m.Gas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Gas |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTraceTxRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryTraceTxRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTraceTxRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Msg", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Msg == nil { + m.Msg = &MsgEthereumTx{} + } + if err := m.Msg.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TraceConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TraceConfig == nil { + m.TraceConfig = &TraceConfig{} + } + if err := m.TraceConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Predecessors", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Predecessors = append(m.Predecessors, &MsgEthereumTx{}) + if err := m.Predecessors[len(m.Predecessors)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockNumber", wireType) + } + m.BlockNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockNumber |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BlockHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.BlockTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposerAddress", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProposerAddress = append(m.ProposerAddress[:0], dAtA[iNdEx:postIndex]...) + if m.ProposerAddress == nil { + m.ProposerAddress = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockMaxGas", wireType) + } + m.BlockMaxGas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockMaxGas |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTraceTxResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryTraceTxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTraceTxResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTraceBlockRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryTraceBlockRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTraceBlockRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Txs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Txs = append(m.Txs, &MsgEthereumTx{}) + if err := m.Txs[len(m.Txs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TraceConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TraceConfig == nil { + m.TraceConfig = &TraceConfig{} + } + if err := m.TraceConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockNumber", wireType) + } + m.BlockNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockNumber |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BlockHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.BlockTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposerAddress", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProposerAddress = append(m.ProposerAddress[:0], dAtA[iNdEx:postIndex]...) + if m.ProposerAddress == nil { + m.ProposerAddress = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockMaxGas", wireType) + } + m.BlockMaxGas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockMaxGas |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryTraceBlockResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryTraceBlockResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryTraceBlockResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryBaseFeeRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryBaseFeeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBaseFeeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryBaseFeeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryBaseFeeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBaseFeeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.BaseFee = &v + if err := m.BaseFee.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BaseFeeUnibi", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.BaseFeeUnibi = &v + if err := m.BaseFeeUnibi.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryFunTokenMappingRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryFunTokenMappingRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFunTokenMappingRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Token = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryFunTokenMappingResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryFunTokenMappingResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryFunTokenMappingResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FunToken", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.FunToken == nil { + m.FunToken = &FunToken{} + } + if err := m.FunToken.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/evm/query.pb.gw.go b/x/evm/query.pb.gw.go new file mode 100644 index 000000000..7ed15c0b1 --- /dev/null +++ b/x/evm/query.pb.gw.go @@ -0,0 +1,1261 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: eth/evm/v1/query.proto + +/* +Package evm is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package evm + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_EthAccount_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEthAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := client.EthAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_EthAccount_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEthAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := server.EthAccount(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ValidatorAccount_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["cons_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "cons_address") + } + + protoReq.ConsAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "cons_address", err) + } + + msg, err := client.ValidatorAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ValidatorAccount_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorAccountRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["cons_address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "cons_address") + } + + protoReq.ConsAddress, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "cons_address", err) + } + + msg, err := server.ValidatorAccount(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Balance_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBalanceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := client.Balance(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Balance_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBalanceRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := server.Balance(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Storage_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryStorageRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + val, ok = pathParams["key"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "key") + } + + protoReq.Key, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "key", err) + } + + msg, err := client.Storage(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Storage_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryStorageRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + val, ok = pathParams["key"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "key") + } + + protoReq.Key, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "key", err) + } + + msg, err := server.Storage(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Code_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCodeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := client.Code(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Code_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCodeRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["address"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "address") + } + + protoReq.Address, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "address", err) + } + + msg, err := server.Code(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_EthCall_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_EthCall_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EthCallRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EthCall_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.EthCall(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_EthCall_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EthCallRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EthCall_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.EthCall(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_EstimateGas_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_EstimateGas_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EthCallRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EstimateGas_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.EstimateGas(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_EstimateGas_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq EthCallRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_EstimateGas_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.EstimateGas(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_TraceTx_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_TraceTx_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTraceTxRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TraceTx_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.TraceTx(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TraceTx_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTraceTxRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TraceTx_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.TraceTx(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_TraceBlock_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_TraceBlock_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTraceBlockRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TraceBlock_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.TraceBlock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TraceBlock_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTraceBlockRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TraceBlock_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.TraceBlock(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Query_TraceCall_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_TraceCall_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTraceTxRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TraceCall_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.TraceCall(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_TraceCall_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryTraceTxRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_TraceCall_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.TraceCall(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BaseFee_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBaseFeeRequest + var metadata runtime.ServerMetadata + + msg, err := client.BaseFee(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BaseFee_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBaseFeeRequest + var metadata runtime.ServerMetadata + + msg, err := server.BaseFee(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_FunTokenMapping_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFunTokenMappingRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["token"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "token") + } + + protoReq.Token, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "token", err) + } + + msg, err := client.FunTokenMapping(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_FunTokenMapping_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryFunTokenMappingRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["token"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "token") + } + + protoReq.Token, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "token", err) + } + + msg, err := server.FunTokenMapping(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_EthAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_EthAccount_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EthAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ValidatorAccount_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ValidatorAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Balance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Balance_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Balance_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Storage_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Storage_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Storage_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Code_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Code_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Code_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EthCall_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_EthCall_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EthCall_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EstimateGas_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_EstimateGas_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EstimateGas_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TraceTx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_TraceTx_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TraceTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TraceBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_TraceBlock_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TraceBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TraceCall_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_TraceCall_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TraceCall_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BaseFee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_BaseFee_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_BaseFee_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FunTokenMapping_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_FunTokenMapping_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_FunTokenMapping_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_EthAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_EthAccount_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EthAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ValidatorAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ValidatorAccount_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ValidatorAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Balance_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Balance_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Balance_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Storage_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Storage_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Storage_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Code_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Code_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Code_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EthCall_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_EthCall_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EthCall_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_EstimateGas_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_EstimateGas_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EstimateGas_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TraceTx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_TraceTx_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TraceTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TraceBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_TraceBlock_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TraceBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_TraceCall_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_TraceCall_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_TraceCall_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BaseFee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_BaseFee_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_BaseFee_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_FunTokenMapping_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_FunTokenMapping_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_FunTokenMapping_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_EthAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"nibiru", "evm", "v1", "eth_account", "address"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ValidatorAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"nibiru", "evm", "v1", "validator_account", "cons_address"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Balance_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"nibiru", "evm", "v1", "balances", "address"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Storage_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"nibiru", "evm", "v1", "storage", "address", "key"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Code_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"nibiru", "evm", "v1", "codes", "address"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "evm", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_EthCall_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "evm", "v1", "eth_call"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_EstimateGas_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "evm", "v1", "estimate_gas"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_TraceTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "evm", "v1", "trace_tx"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_TraceBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "evm", "v1", "trace_block"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_TraceCall_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "evm", "v1", "trace_call"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BaseFee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "evm", "v1", "base_fee"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_FunTokenMapping_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"nibiru", "evm", "v1", "funtoken", "token"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_EthAccount_0 = runtime.ForwardResponseMessage + + forward_Query_ValidatorAccount_0 = runtime.ForwardResponseMessage + + forward_Query_Balance_0 = runtime.ForwardResponseMessage + + forward_Query_Storage_0 = runtime.ForwardResponseMessage + + forward_Query_Code_0 = runtime.ForwardResponseMessage + + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_EthCall_0 = runtime.ForwardResponseMessage + + forward_Query_EstimateGas_0 = runtime.ForwardResponseMessage + + forward_Query_TraceTx_0 = runtime.ForwardResponseMessage + + forward_Query_TraceBlock_0 = runtime.ForwardResponseMessage + + forward_Query_TraceCall_0 = runtime.ForwardResponseMessage + + forward_Query_BaseFee_0 = runtime.ForwardResponseMessage + + forward_Query_FunTokenMapping_0 = runtime.ForwardResponseMessage +) diff --git a/x/evm/state.go b/x/evm/state.go new file mode 100644 index 000000000..892e260ab --- /dev/null +++ b/x/evm/state.go @@ -0,0 +1,67 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "fmt" + "strings" + + errorsmod "cosmossdk.io/errors" + "github.com/ethereum/go-ethereum/common" +) + +// Storage represents the account Storage map as a slice of single key value +// State pairs. This is to prevent non determinism at genesis initialization or export. +type Storage []State + +// Validate performs a basic validation of the Storage fields. +func (s Storage) Validate() error { + seenStorage := make(map[string]bool) + for i, state := range s { + if seenStorage[state.Key] { + return errorsmod.Wrapf(ErrInvalidState, "duplicate state key %d: %s", i, state.Key) + } + + if err := state.Validate(); err != nil { + return err + } + + seenStorage[state.Key] = true + } + return nil +} + +// String implements the stringer interface +func (s Storage) String() string { + var str string + for _, state := range s { + str += fmt.Sprintf("%s\n", state.String()) + } + + return str +} + +// Copy returns a copy of storage. +func (s Storage) Copy() Storage { + cpy := make(Storage, len(s)) + copy(cpy, s) + + return cpy +} + +// Validate performs a basic validation of the State fields. Note that [State] +// can be empty. +func (s State) Validate() error { + if strings.TrimSpace(s.Key) == "" { + return errorsmod.Wrap(ErrInvalidState, "state key hash cannot be blank") + } + + return nil +} + +// NewStateFromEthHashes creates a [State] struct from Eth hashes. +func NewStateFromEthHashes(key, value common.Hash) State { + return State{ + Key: key.String(), + Value: value.String(), + } +} diff --git a/x/evm/state_test.go b/x/evm/state_test.go new file mode 100644 index 000000000..482ef512a --- /dev/null +++ b/x/evm/state_test.go @@ -0,0 +1,92 @@ +package evm + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" +) + +type SuiteStorage struct { + suite.Suite +} + +func TestSuiteStorage(t *testing.T) { + suite.Run(t, new(SuiteStorage)) +} + +func (s *SuiteStorage) TestStorageString() { + storage := Storage{NewStateFromEthHashes(common.BytesToHash([]byte("key")), common.BytesToHash([]byte("value")))} + str := "key:\"0x00000000000000000000000000000000000000000000000000000000006b6579\" value:\"0x00000000000000000000000000000000000000000000000000000076616c7565\" \n" + s.Equal(str, storage.String()) +} + +func (s *SuiteStorage) TestStorageValidate() { + testCases := []struct { + name string + storage Storage + wantPass bool + }{ + { + name: "valid storage", + storage: Storage{ + NewStateFromEthHashes(common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{1, 2, 3})), + }, + wantPass: true, + }, + { + name: "empty storage key bytes", + storage: Storage{ + {Key: ""}, + }, + wantPass: false, + }, + { + name: "duplicated storage key", + storage: Storage{ + {Key: common.BytesToHash([]byte{1, 2, 3}).String()}, + {Key: common.BytesToHash([]byte{1, 2, 3}).String()}, + }, + wantPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.storage.Validate() + if tc.wantPass { + s.NoError(err, tc.name) + } else { + s.Error(err, tc.name) + } + } +} + +func (s *SuiteStorage) TestStorageCopy() { + testCases := []struct { + name string + storage Storage + }{ + { + "single storage", + Storage{ + NewStateFromEthHashes(common.BytesToHash([]byte{1, 2, 3}), common.BytesToHash([]byte{1, 2, 3})), + }, + }, + { + "empty storage key value bytes", + Storage{ + {Key: common.Hash{}.String(), Value: common.Hash{}.String()}, + }, + }, + { + "empty storage", + Storage{}, + }, + } + + for _, tc := range testCases { + tc := tc + s.Require().Equal(tc.storage, tc.storage.Copy(), tc.name) + } +} diff --git a/x/evm/statedb/access_list.go b/x/evm/statedb/access_list.go new file mode 100644 index 000000000..f62b45171 --- /dev/null +++ b/x/evm/statedb/access_list.go @@ -0,0 +1,118 @@ +package statedb + +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +import ( + "github.com/ethereum/go-ethereum/common" +) + +type accessList struct { + addresses map[common.Address]int + slots []map[common.Hash]struct{} +} + +// ContainsAddress returns true if the address is in the access list. +func (al *accessList) ContainsAddress(address common.Address) bool { + _, ok := al.addresses[address] + return ok +} + +// Contains checks if a slot within an account is present in the access list, returning +// separate flags for the presence of the account and the slot respectively. +func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { + idx, ok := al.addresses[address] + if !ok { + // no such address (and hence zero slots) + return false, false + } + if idx == -1 { + // address yes, but no slots + return true, false + } + _, slotPresent = al.slots[idx][slot] + return true, slotPresent +} + +// newAccessList creates a new accessList. +func newAccessList() *accessList { + return &accessList{ + addresses: make(map[common.Address]int), + } +} + +// AddAddress adds an address to the access list, and returns 'true' if the operation +// caused a change (addr was not previously in the list). +func (al *accessList) AddAddress(address common.Address) bool { + if _, present := al.addresses[address]; present { + return false + } + al.addresses[address] = -1 + return true +} + +// AddSlot adds the specified (addr, slot) combo to the access list. +// Return values are: +// - address added +// - slot added +// For any 'true' value returned, a corresponding journal entry must be made. +func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) { + idx, addrPresent := al.addresses[address] + if !addrPresent || idx == -1 { + // Address not present, or addr present but no slots there + al.addresses[address] = len(al.slots) + slotmap := map[common.Hash]struct{}{slot: {}} + al.slots = append(al.slots, slotmap) + return !addrPresent, true + } + // There is already an (address,slot) mapping + slotmap := al.slots[idx] + if _, ok := slotmap[slot]; !ok { + slotmap[slot] = struct{}{} + // Journal add slot change + return false, true + } + // No changes required + return false, false +} + +// DeleteSlot removes an (address, slot)-tuple from the access list. +// This operation needs to be performed in the same order as the addition happened. +// This method is meant to be used by the journal, which maintains ordering of +// operations. +func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) { + idx, addrOk := al.addresses[address] + if !addrOk { + panic("reverting slot change, address not present in list") + } + slotmap := al.slots[idx] + delete(slotmap, slot) + // If that was the last (first) slot, remove it + // Since additions and rollbacks are always performed in order, + // we can delete the item without worrying about screwing up later indices + if len(slotmap) == 0 { + al.slots = al.slots[:idx] + al.addresses[address] = -1 + } +} + +// DeleteAddress removes an address from the access list. This operation +// needs to be performed in the same order as the addition happened. +// This method is meant to be used by the journal, which maintains ordering of +// operations. +func (al *accessList) DeleteAddress(address common.Address) { + delete(al.addresses, address) +} diff --git a/x/evm/statedb/config.go b/x/evm/statedb/config.go new file mode 100644 index 000000000..417e480ac --- /dev/null +++ b/x/evm/statedb/config.go @@ -0,0 +1,49 @@ +package statedb + +// Copyright (c) 2023-2024 Nibi, Inc. + +import ( + "math/big" + + gethcommon "github.com/ethereum/go-ethereum/common" + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// TxConfig encapsulates the readonly information of current tx for `StateDB`. +type TxConfig struct { + BlockHash gethcommon.Hash // hash of current block + TxHash gethcommon.Hash // hash of current tx + TxIndex uint // the index of current transaction + LogIndex uint // the index of next log within current block +} + +// NewEmptyTxConfig construct an empty TxConfig, +// used in context where there's no transaction, e.g. `eth_call`/`eth_estimateGas`. +func NewEmptyTxConfig(blockHash gethcommon.Hash) TxConfig { + return TxConfig{ + BlockHash: blockHash, + TxHash: gethcommon.Hash{}, + TxIndex: 0, + LogIndex: 0, + } +} + +// EVMConfig encapsulates parameters needed to create an instance of the EVM +// ("go-ethereum/core/vm.EVM"). +type EVMConfig struct { + Params evm.Params + ChainConfig *gethparams.ChainConfig + + // BlockCoinbase: In Ethereum, the coinbase (or "benficiary") is the address that + // proposed the current block. It corresponds to the [COINBASE op code] + // (the "block.coinbase" stack output). + // + // [COINBASE op code]: https://ethereum.org/en/developers/docs/evm/opcodes/ + BlockCoinbase gethcommon.Address + + // BaseFeeWei is the EVM base fee in units of wei per gas. The term "base + // fee" comes from EIP-1559. + BaseFeeWei *big.Int +} diff --git a/x/evm/statedb/debug.go b/x/evm/statedb/debug.go new file mode 100644 index 000000000..3927e4a5d --- /dev/null +++ b/x/evm/statedb/debug.go @@ -0,0 +1,33 @@ +package statedb + +// Copyright (c) 2023-2024 Nibi, Inc. + +import ( + "github.com/ethereum/go-ethereum/common" +) + +// DebugDirtiesCount is a test helper to inspect how many entries in the journal +// are still dirty (uncommitted). After calling [StateDB.Commit], this function +// should return zero. +func (s *StateDB) DebugDirtiesCount() int { + dirtiesCount := 0 + for _, dirtyCount := range s.Journal.dirties { + dirtiesCount += dirtyCount + } + return dirtiesCount +} + +// DebugDirties is a test helper that returns the journal's dirty account changes map. +func (s *StateDB) DebugDirties() map[common.Address]int { + return s.Journal.dirties +} + +// DebugStateObjects is a test helper that returns returns a copy of the +// [StateDB.stateObjects] map. +func (s *StateDB) DebugStateObjects() map[common.Address]*stateObject { + copyOfMap := make(map[common.Address]*stateObject) + for key, val := range s.stateObjects { + copyOfMap[key] = val + } + return copyOfMap +} diff --git a/x/evm/statedb/interfaces.go b/x/evm/statedb/interfaces.go new file mode 100644 index 000000000..c242771ca --- /dev/null +++ b/x/evm/statedb/interfaces.go @@ -0,0 +1,33 @@ +package statedb + +// Copyright (c) 2023-2024 Nibi, Inc. + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" +) + +// Keeper provide underlying storage of StateDB +type Keeper interface { + // GetAccount: Ethereum account getter for a [statedb.Account]. + GetAccount(ctx sdk.Context, addr common.Address) *Account + GetState(ctx sdk.Context, addr common.Address, key common.Hash) common.Hash + GetCode(ctx sdk.Context, codeHash common.Hash) []byte + + // ForEachStorage: Iterator over contract storage. + ForEachStorage( + ctx sdk.Context, addr common.Address, + stopIter func(key, value common.Hash) bool, + ) + + SetAccount(ctx sdk.Context, addr common.Address, account Account) error + SetState(ctx sdk.Context, addr common.Address, key common.Hash, value []byte) + // SetCode: Setter for smart contract bytecode. Delete if code is empty. + SetCode(ctx sdk.Context, codeHash []byte, code []byte) + + // DeleteAccount handles contract's suicide call, clearing the balance, + // contract bytecode, contract state, and its native account. + DeleteAccount(ctx sdk.Context, addr common.Address) error + + IsPrecompile(addr common.Address) bool +} diff --git a/x/evm/statedb/journal.go b/x/evm/statedb/journal.go new file mode 100644 index 000000000..acbba5eab --- /dev/null +++ b/x/evm/statedb/journal.go @@ -0,0 +1,363 @@ +package statedb + +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +import ( + "bytes" + "math/big" + "sort" + + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" +) + +// JournalChange, also called a "journal entry", is a modification entry in the +// state change journal that can be reverted on demand. +type JournalChange interface { + // Revert undoes the changes introduced by this journal entry. + Revert(*StateDB) + + // Dirtied returns the Ethereum address modified by this journal entry. + Dirtied() *common.Address +} + +// journal contains the list of state modifications applied since the last state +// commit. These are tracked to be able to be reverted in the case of an execution +// exception or request for reversal. +type journal struct { + entries []JournalChange // Current changes tracked by the journal + dirties map[common.Address]int // Dirty accounts and the number of changes +} + +// newJournal creates a new initialized journal. +func newJournal() *journal { + return &journal{ + dirties: make(map[common.Address]int), + } +} + +// sortedDirties sort the dirty addresses for deterministic iteration +func (j *journal) sortedDirties() []common.Address { + keys := make([]common.Address, 0, len(j.dirties)) + for k := range j.dirties { + keys = append(keys, k) + } + sort.Slice(keys, func(i, j int) bool { + return bytes.Compare(keys[i].Bytes(), keys[j].Bytes()) < 0 + }) + return keys +} + +// append inserts a new modification entry to the end of the change journal. +func (j *journal) append(entry JournalChange) { + j.entries = append(j.entries, entry) + if addr := entry.Dirtied(); addr != nil { + j.dirties[*addr]++ + } +} + +// Revert undoes a batch of journalled modifications along with any Reverted +// dirty handling too. +func (j *journal) Revert(statedb *StateDB, snapshot int) { + for i := len(j.entries) - 1; i >= snapshot; i-- { + // Undo the changes made by the operation + j.entries[i].Revert(statedb) + + // Drop any dirty tracking induced by the change + if addr := j.entries[i].Dirtied(); addr != nil { + if j.dirties[*addr]--; j.dirties[*addr] == 0 { + delete(j.dirties, *addr) + } + } + } + j.entries = j.entries[:snapshot] +} + +// Length returns the current number of entries in the journal. +func (j *journal) Length() int { + return len(j.entries) +} + +// ------------------------------------------------------ +// createObjectChange + +// createObjectChange: [JournalChange] implementation for when +// a new account (called an "object" in this context) is created in state. +type createObjectChange struct { + account *common.Address +} + +var _ JournalChange = createObjectChange{} + +func (ch createObjectChange) Revert(s *StateDB) { + delete(s.stateObjects, *ch.account) +} + +func (ch createObjectChange) Dirtied() *common.Address { + return ch.account +} + +// ------------------------------------------------------ +// resetObjectChange + +// resetObjectChange: [JournalChange] for an account that needs its +// original state reset. This is used when an account's state is being replaced +// and we need to revert to the previous version. +type resetObjectChange struct { + prev *stateObject +} + +var _ JournalChange = resetObjectChange{} + +func (ch resetObjectChange) Revert(s *StateDB) { + s.setStateObject(ch.prev) +} + +func (ch resetObjectChange) Dirtied() *common.Address { + return nil +} + +// ------------------------------------------------------ +// suicideChange + +type suicideChange struct { + account *common.Address + prev bool // whether account had already suicided + prevbalance *big.Int +} + +var _ JournalChange = suicideChange{} + +func (ch suicideChange) Revert(s *StateDB) { + obj := s.getStateObject(*ch.account) + if obj != nil { + obj.Suicided = ch.prev + obj.setBalance(ch.prevbalance) + } +} + +func (ch suicideChange) Dirtied() *common.Address { + return ch.account +} + +// ------------------------------------------------------ +// balanceChange + +// balanceChange: [JournalChange] for an update to the wei balance of an account. +type balanceChange struct { + account *common.Address + prevWei *big.Int +} + +var _ JournalChange = balanceChange{} + +func (ch balanceChange) Revert(s *StateDB) { + s.getStateObject(*ch.account).setBalance(ch.prevWei) +} + +func (ch balanceChange) Dirtied() *common.Address { + return ch.account +} + +// ------------------------------------------------------ +// nonceChange + +// nonceChange: [JournalChange] for an update to the nonce of an account. +// The nonce is a counter of the number of transactions sent from an account. +type nonceChange struct { + account *common.Address + prev uint64 +} + +var _ JournalChange = nonceChange{} + +func (ch nonceChange) Revert(s *StateDB) { + s.getStateObject(*ch.account).setNonce(ch.prev) +} + +func (ch nonceChange) Dirtied() *common.Address { + return ch.account +} + +// ------------------------------------------------------ +// codeChange + +// codeChange: [JournalChange] for an update to an account's code (smart contract +// bytecode). The previous code and hash for the code are stored to enable +// reversion. +type codeChange struct { + account *common.Address + prevcode, prevhash []byte +} + +var _ JournalChange = codeChange{} + +func (ch codeChange) Revert(s *StateDB) { + s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) +} + +func (ch codeChange) Dirtied() *common.Address { + return ch.account +} + +// ------------------------------------------------------ +// storageChange + +// storageChange: [JournalChange] for the modification of a single key and value +// within a contract's storage. +type storageChange struct { + account *common.Address + key, prevalue common.Hash +} + +var _ JournalChange = storageChange{} + +func (ch storageChange) Revert(s *StateDB) { + s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) +} + +func (ch storageChange) Dirtied() *common.Address { + return ch.account +} + +// ------------------------------------------------------ +// refundChange + +// refundChange: [JournalChange] for the global gas refund counter. +// This tracks changes to the gas refund value during contract execution. +type refundChange struct { + prev uint64 +} + +var _ JournalChange = refundChange{} + +func (ch refundChange) Revert(s *StateDB) { + s.refund = ch.prev +} + +func (ch refundChange) Dirtied() *common.Address { + return nil +} + +// ------------------------------------------------------ +// addLogChange + +// addLogChange represents [JournalChange] for a new log addition. +// When reverted, it removes the last log from the accumulated logs list. +type addLogChange struct{} + +var _ JournalChange = addLogChange{} + +func (ch addLogChange) Revert(s *StateDB) { + s.logs = s.logs[:len(s.logs)-1] +} + +func (ch addLogChange) Dirtied() *common.Address { + return nil +} + +// ------------------------------------------------------ +// accessListAddAccountChange + +// accessListAddAccountChange represents [JournalChange] for when an address +// is added to the access list. Access lists track warm storage slots for +// gas cost calculations. +type accessListAddAccountChange struct { + address *common.Address +} + +// When an (address, slot) combination is added, it always results in two +// journal entries if the address is not already present: +// 1. `accessListAddAccountChange`: a journal change for the address +// 2. `accessListAddSlotChange`: a journal change for the (address, slot) +// combination. +// +// Thus, when reverting, we can safely delete the address, as no storage slots +// remain once the address entry is reverted. +func (ch accessListAddAccountChange) Revert(s *StateDB) { + s.accessList.DeleteAddress(*ch.address) +} + +func (ch accessListAddAccountChange) Dirtied() *common.Address { + return nil +} + +// ------------------------------------------------------ +// accessListAddSlotChange + +// accessListAddSlotChange: [JournalChange] implementations for +type accessListAddSlotChange struct { + address *common.Address + slot *common.Hash +} + +// accessListAddSlotChange represents a [JournalChange] for when a storage slot +// is added to an address's access list entry. This tracks individual storage +// slots that have been accessed. +var _ JournalChange = accessListAddSlotChange{} + +func (ch accessListAddSlotChange) Revert(s *StateDB) { + s.accessList.DeleteSlot(*ch.address, *ch.slot) +} + +func (ch accessListAddSlotChange) Dirtied() *common.Address { + return nil +} + +// ------------------------------------------------------ +// PrecompileSnapshotBeforeRun + +// PrecompileCalled: Precompiles can alter persistent storage of other +// modules. These changes to persistent storage are not reverted by a `Revert` of +// [JournalChange] by default, as it generally manages only changes to accounts +// and Bank balances for ether (NIBI). +// +// As a workaround to make state changes from precompiles reversible, we store +// [PrecompileCalled] snapshots that sync and record the prior state +// of the other modules, allowing precompile calls to truly be reverted. +// +// As a simple example, suppose that a transaction calls a precompile. +// 1. If the precompile changes the state in the Bank Module or Wasm module +// 2. The call gets reverted (`revert()` in Solidity), which shoud restore the +// state to a in-memory snapshot recorded on the StateDB journal. +// 3. This could cause a problem where changes to the rest of the blockchain state +// are still in effect following the reversion in the EVM state DB. +type PrecompileCalled struct { + MultiStore store.CacheMultiStore + Events sdk.Events +} + +var _ JournalChange = PrecompileCalled{} + +// Revert rolls back the [StateDB] cache context to the state it was in prior to +// the precompile call. Modifications to this cache context are pushed to the +// commit context (s.evmTxCtx) when [StateDB.Commit] is executed. +func (ch PrecompileCalled) Revert(s *StateDB) { + s.cacheCtx = s.cacheCtx.WithMultiStore(ch.MultiStore) + // Rewrite the `writeCacheCtxFn` using the same logic as sdk.Context.CacheCtx + s.writeToCommitCtxFromCacheCtx = func() { + s.evmTxCtx.EventManager().EmitEvents(ch.Events) + // TODO: Check correctness of the emitted events + // https://github.com/NibiruChain/nibiru/issues/2096 + ch.MultiStore.Write() + } +} + +func (ch PrecompileCalled) Dirtied() *common.Address { + return nil +} diff --git a/x/evm/statedb/journal_test.go b/x/evm/statedb/journal_test.go new file mode 100644 index 000000000..e10505a00 --- /dev/null +++ b/x/evm/statedb/journal_test.go @@ -0,0 +1,273 @@ +package statedb_test + +import ( + "fmt" + "math/big" + "strings" + "testing" + + "github.com/MakeNowJust/heredoc/v2" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/core/vm" + + serverconfig "github.com/NibiruChain/nibiru/v2/app/server/config" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile/test" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +func (s *Suite) TestCommitRemovesDirties() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_ERC20Minter, + "name", + "SYMBOL", + uint8(18), + ) + s.Require().NoError(err, deployResp) + erc20 := deployResp.ContractAddr + + input, err := deps.EvmKeeper.ERC20().ABI.Pack("mint", deps.Sender.EthAddr, big.NewInt(69_420)) + s.Require().NoError(err) + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, // caller + &erc20, // contract + true, // commit + input, + keeper.Erc20GasLimitExecute, + ) + s.Require().NoError(err) + s.Require().EqualValues(0, evmObj.StateDB.(*statedb.StateDB).DebugDirtiesCount()) +} + +func (s *Suite) TestCommitRemovesDirties_OnlyStateDB() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + stateDB := evmObj.StateDB.(*statedb.StateDB) + + randomAcc := evmtest.NewEthPrivAcc().EthAddr + balDelta := evm.NativeToWei(big.NewInt(4)) + // 2 dirties from [createObjectChange, balanceChange] + stateDB.AddBalance(randomAcc, balDelta) + // 1 dirties from [balanceChange] + stateDB.AddBalance(randomAcc, balDelta) + // 1 dirties from [balanceChange] + stateDB.SubBalance(randomAcc, balDelta) + if stateDB.DebugDirtiesCount() != 4 { + debugDirtiesCountMismatch(stateDB, s.T()) + s.FailNow("expected 4 dirty journal changes") + } + + s.T().Log("StateDB.Commit, then Dirties should be gone") + err := stateDB.Commit() + s.NoError(err) + if stateDB.DebugDirtiesCount() != 0 { + debugDirtiesCountMismatch(stateDB, s.T()) + s.FailNow("expected 0 dirty journal changes") + } +} + +func (s *Suite) TestContractCallsAnotherContract() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + stateDB := evmObj.StateDB.(*statedb.StateDB) + + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420))), + )) + + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_ERC20Minter, + "name", + "SYMBOL", + uint8(18), + ) + s.Require().NoError(err, deployResp) + erc20 := deployResp.ContractAddr + + s.Run("Mint 69_420 tokens", func() { + contractInput, err := deps.EvmKeeper.ERC20().ABI.Pack("mint", deps.Sender.EthAddr, big.NewInt(69_420)) + s.Require().NoError(err) + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, // caller + &erc20, // contract + true, // commit + contractInput, + keeper.Erc20GasLimitExecute, + ) + s.Require().NoError(err) + }) + + randomAcc := evmtest.NewEthPrivAcc().EthAddr + contractInput, err := embeds.SmartContract_ERC20Minter.ABI.Pack("transfer", randomAcc, big.NewInt(69_000)) + s.Require().NoError(err) + + s.Run("Transfer 69_000 tokens", func() { + s.T().Log("Transfer 69_000 tokens") + + _, _, err = evmObj.Call( + vm.AccountRef(deps.Sender.EthAddr), + erc20, + contractInput, + serverconfig.DefaultEthCallGasLimit, + big.NewInt(0), + ) + s.Require().NoError(err) + if stateDB.DebugDirtiesCount() != 2 { + debugDirtiesCountMismatch(stateDB, s.T()) + s.FailNowf("expected 2 dirty journal changes", "%#v", stateDB.Journal) + } + }) + + s.Run("Transfer 69_000 tokens", func() { + // The contract calling itself is invalid in this context. + // Note the comment in vm.Contract: + // + // type Contract struct { + // // CallerAddress is the result of the caller which initialized this + // // contract. However when the "call method" is delegated this value + // // needs to be initialized to that of the caller's caller. + // CallerAddress common.Address + // // ... + // } + // // + + _, _, err = evmObj.Call( + vm.AccountRef(erc20), + erc20, + contractInput, + serverconfig.DefaultEthCallGasLimit, + big.NewInt(0), + ) + s.Require().ErrorContains(err, vm.ErrExecutionReverted.Error()) + }) +} + +func (s *Suite) TestJournalReversion() { + deps := evmtest.NewTestDeps() + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420))), + )) + + s.T().Log("Set up helloworldcounter.wasm") + wasmContracts := test.SetupWasmContracts(&deps, &s.Suite) + helloWorldCounterWasm := wasmContracts[1] + fmt.Printf("wasmContract: %s\n", helloWorldCounterWasm) + + s.T().Log("commitEvmTx=true, expect 0 dirty journal entries") + evmObj, stateDB := deps.NewEVM() + test.IncrementWasmCounterWithExecuteMulti( + &s.Suite, &deps, evmObj, helloWorldCounterWasm, 7, true, + ) + if stateDB.DebugDirtiesCount() != 0 { + debugDirtiesCountMismatch(stateDB, s.T()) + s.FailNowf("statedb dirty count mismatch", "expected 0 dirty journal changes, but instead got: %d", stateDB.DebugDirtiesCount()) + } + + s.T().Log("commitEvmTx=false, expect dirty journal entries") + evmObj, stateDB = deps.NewEVM() + test.IncrementWasmCounterWithExecuteMulti( + &s.Suite, &deps, evmObj, helloWorldCounterWasm, 5, false, + ) + s.T().Log("Expect exactly 1 dirty journal entry for the precompile snapshot") + if stateDB.DebugDirtiesCount() != 1 { + debugDirtiesCountMismatch(stateDB, s.T()) + s.FailNowf("statedb dirty count mismatch", "expected 1 dirty journal change, but instead got: %d", stateDB.DebugDirtiesCount()) + } + + s.T().Log("Expect to see the pending changes included in the EVM context") + test.AssertWasmCounterStateWithEvm( + &s.Suite, deps, evmObj, helloWorldCounterWasm, 7+5, + ) + s.T().Log("Expect to see the pending changes not included in cosmos ctx") + test.AssertWasmCounterState( + &s.Suite, deps, helloWorldCounterWasm, 7, + ) + + // NOTE: that the [StateDB.Commit] fn has not been called yet. We're still + // mid-transaction. + + s.T().Log("EVM revert operation should bring about the old state") + test.IncrementWasmCounterWithExecuteMulti( + &s.Suite, &deps, evmObj, helloWorldCounterWasm, 50, false, + ) + s.T().Log(heredoc.Doc(`At this point, 2 precompile calls have succeeded. +One that increments the counter to 7 + 5, and another for +50. +The StateDB has not been committed. We expect to be able to revert to both +snapshots and see the prior states.`)) + test.AssertWasmCounterStateWithEvm( + &s.Suite, deps, evmObj, helloWorldCounterWasm, 7+5+50, + ) + + errFn := common.TryCatch(func() { + // a revision that doesn't exist + stateDB.RevertToSnapshot(9000) + }) + s.Require().ErrorContains(errFn(), "revision id 9000 cannot be reverted") + + stateDB.RevertToSnapshot(2) + test.AssertWasmCounterStateWithEvm( + &s.Suite, deps, evmObj, helloWorldCounterWasm, 7+5, + ) + + stateDB.RevertToSnapshot(0) + test.AssertWasmCounterStateWithEvm( + &s.Suite, deps, evmObj, helloWorldCounterWasm, 7, + ) + + s.Require().NoError(stateDB.Commit()) + s.Require().EqualValues(0, stateDB.DebugDirtiesCount()) + test.AssertWasmCounterState( + &s.Suite, deps, helloWorldCounterWasm, 7, + ) +} + +func debugDirtiesCountMismatch(db *statedb.StateDB, t *testing.T) { + lines := []string{} + dirties := db.DebugDirties() + stateObjects := db.DebugStateObjects() + for addr, dirtyCount := range dirties { + lines = append(lines, fmt.Sprintf("Dirty addr: %s, dirtyCount=%d", addr, dirtyCount)) + + // Inspect the actual state object + maybeObj := stateObjects[addr] + if maybeObj == nil { + lines = append(lines, " no state object found!") + continue + } + obj := *maybeObj + + lines = append(lines, fmt.Sprintf(" balance: %s", obj.Balance())) + lines = append(lines, fmt.Sprintf(" suicided: %v", obj.Suicided)) + lines = append(lines, fmt.Sprintf(" dirtyCode: %v", obj.DirtyCode)) + + // Print storage state + lines = append(lines, fmt.Sprintf(" len(obj.DirtyStorage) entries: %d", len(obj.DirtyStorage))) + for k, v := range obj.DirtyStorage { + lines = append(lines, fmt.Sprintf(" key: %s, value: %s", k.Hex(), v.Hex())) + origVal := obj.OriginStorage[k] + lines = append(lines, fmt.Sprintf(" origin value: %s", origVal.Hex())) + } + } + + t.Log("debugDirtiesCountMismatch:\n", strings.Join(lines, "\n")) +} diff --git a/x/evm/statedb/state_object.go b/x/evm/statedb/state_object.go new file mode 100644 index 000000000..28ba2d85a --- /dev/null +++ b/x/evm/statedb/state_object.go @@ -0,0 +1,295 @@ +package statedb + +// Copyright (c) 2023-2024 Nibi, Inc. + +import ( + "bytes" + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +var emptyCodeHash = crypto.Keccak256(nil) + +// Account represents an Ethereum account as viewed by the Auth module state. The +// balance is stored in the smallest native unit (e.g., micronibi or unibi). +// These objects are stored in the storage of auth module. +type Account struct { + // BalanceNative is the micronibi (unibi) balance of the account, which is + // the official balance in the x/bank module state + BalanceNative *big.Int + // Nonce is the number of transactions sent from this account or, for contract accounts, the number of contract-creations made by this account + Nonce uint64 + // CodeHash is the hash of the contract code for this account, or nil if it's not a contract account + CodeHash []byte +} + +// AccountWei represents an Ethereum account as viewed by the EVM. This struct is +// derived from an `Account` but represents balances in wei, which is necessary +// for correct operation within the EVM. The EVM expects and operates on wei +// values, which are 10^12 times larger than the native unibi value due to the +// definition of NIBI as "ether". +type AccountWei struct { + BalanceWei *big.Int + // Nonce is the number of transactions sent from this account or, for contract accounts, the number of contract-creations made by this account + Nonce uint64 + // CodeHash is the hash of the contract code for this account, or nil if it's not a contract account + CodeHash []byte +} + +// ToWei converts an Account (native representation) to AccountWei (EVM +// representation). This conversion is necessary when moving from the Cosmos SDK +// context to the EVM context. It multiplies the balance by 10^12 to convert from +// unibi to wei. +func (acc Account) ToWei() AccountWei { + return AccountWei{ + BalanceWei: evm.NativeToWei(acc.BalanceNative), + Nonce: acc.Nonce, + CodeHash: acc.CodeHash, + } +} + +// ToNative converts an AccountWei (EVM representation) back to an Account +// (native representation). This conversion is necessary when moving from the EVM +// context back to the Cosmos SDK context. It divides the balance by 10^12 to +// convert from wei to unibi. +func (acc AccountWei) ToNative() Account { + return Account{ + BalanceNative: evm.WeiToNative(acc.BalanceWei), + Nonce: acc.Nonce, + CodeHash: acc.CodeHash, + } +} + +// NewEmptyAccount returns an empty account. +func NewEmptyAccount() *Account { + return &Account{ + BalanceNative: new(big.Int), + CodeHash: emptyCodeHash, + } +} + +// IsContract returns if the account contains contract code. +func (acct *Account) IsContract() bool { + return (acct != nil) && !bytes.Equal(acct.CodeHash, emptyCodeHash) +} + +// Storage represents in-memory cache/buffer of contract storage. +type Storage map[common.Hash]common.Hash + +// SortedKeys sort the keys for deterministic iteration +func (s Storage) SortedKeys() []common.Hash { + keys := make([]common.Hash, 0, len(s)) + for k := range s { + keys = append(keys, k) + } + sort.Slice(keys, func(i, j int) bool { + return bytes.Compare(keys[i].Bytes(), keys[j].Bytes()) < 0 + }) + return keys +} + +// stateObject represents the state of a Nibiru EVM account. +// It encapsulates both the account data (balance, nonce, code) and the contract +// storage state. stateObject serves as an in-memory cache and staging area for +// changes before they are committed to the underlying storage. +// +// Key features: +// 1. It uses AccountWei, which represents balances in wei for EVM compatibility. +// 2. It maintains both the original (committed) storage and dirty (uncommitted) storage. +// 3. It tracks whether the account has been marked for deletion (suicided). +// 4. It caches the contract code for efficient access. +// +// stateObjects are used to: +// - Efficiently manage and track changes to account state during EVM execution. +// - Provide a layer of abstraction between the EVM and the underlying storage. +// - Enable features like state reverting and snapshotting. +// - Optimize performance by minimizing direct access to the underlying storage. +type stateObject struct { + db *StateDB + + account AccountWei + code []byte + + // state storage + OriginStorage Storage + DirtyStorage Storage + + address common.Address + + // flags + DirtyCode bool + Suicided bool +} + +// newObject creates a state object. +func newObject(db *StateDB, address common.Address, account Account) *stateObject { + if account.BalanceNative == nil { + account.BalanceNative = new(big.Int) + } + if account.CodeHash == nil { + account.CodeHash = emptyCodeHash + } + return &stateObject{ + db: db, + address: address, + // Reflect the micronibi (unibi) balance in wei + account: account.ToWei(), + OriginStorage: make(Storage), + DirtyStorage: make(Storage), + } +} + +// isEmpty returns whether the account is considered isEmpty. +func (s *stateObject) isEmpty() bool { + return s.account.Nonce == 0 && + s.account.BalanceWei.Sign() == 0 && + bytes.Equal(s.account.CodeHash, emptyCodeHash) +} + +// AddBalance adds amount to s's balance. +// It is used to add funds to the destination account of a transfer. +func (s *stateObject) AddBalance(amount *big.Int) { + if amount.Sign() == 0 { + return + } + s.SetBalance(new(big.Int).Add(s.Balance(), amount)) +} + +// SubBalance removes amount from s's balance. +// It is used to remove funds from the origin account of a transfer. +func (s *stateObject) SubBalance(amount *big.Int) { + if amount.Sign() == 0 { + return + } + s.SetBalance(new(big.Int).Sub(s.Balance(), amount)) +} + +// SetBalance update account balance. +func (s *stateObject) SetBalance(amount *big.Int) { + s.db.Journal.append(balanceChange{ + account: &s.address, + prevWei: new(big.Int).Set(s.account.BalanceWei), + }) + s.setBalance(amount) +} + +func (s *stateObject) setBalance(amount *big.Int) { + s.account.BalanceWei = amount +} + +// +// Attribute accessors +// + +// Address returns the address of the contract/account +func (s *stateObject) Address() common.Address { + return s.address +} + +// Code returns the contract code associated with this object, if any. +func (s *stateObject) Code() []byte { + if s.code != nil { + return s.code + } + if bytes.Equal(s.CodeHash(), emptyCodeHash) { + return nil + } + code := s.db.keeper.GetCode(s.db.evmTxCtx, common.BytesToHash(s.CodeHash())) + s.code = code + return code +} + +// CodeSize returns the size of the contract code associated with this object, +// or zero if none. +func (s *stateObject) CodeSize() int { + return len(s.Code()) +} + +// SetCode set contract code to account +func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { + prevcode := s.Code() + s.db.Journal.append(codeChange{ + account: &s.address, + prevhash: s.CodeHash(), + prevcode: prevcode, + }) + s.setCode(codeHash, code) +} + +func (s *stateObject) setCode(codeHash common.Hash, code []byte) { + s.code = code + s.account.CodeHash = codeHash[:] + s.DirtyCode = true +} + +// SetNonce set nonce to account +func (s *stateObject) SetNonce(nonce uint64) { + s.db.Journal.append(nonceChange{ + account: &s.address, + prev: s.account.Nonce, + }) + s.setNonce(nonce) +} + +func (s *stateObject) setNonce(nonce uint64) { + s.account.Nonce = nonce +} + +// CodeHash returns the code hash of account +func (s *stateObject) CodeHash() []byte { + return s.account.CodeHash +} + +// Balance returns the balance of account +func (s *stateObject) Balance() *big.Int { + return s.account.BalanceWei +} + +// Nonce returns the nonce of account +func (s *stateObject) Nonce() uint64 { + return s.account.Nonce +} + +// GetCommittedState query the committed state +func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { + if value, cached := s.OriginStorage[key]; cached { + return value + } + // If no live objects are available, load it from keeper + value := s.db.keeper.GetState(s.db.evmTxCtx, s.Address(), key) + s.OriginStorage[key] = value + return value +} + +// GetState query the current state (including dirty state) +func (s *stateObject) GetState(key common.Hash) common.Hash { + if value, dirty := s.DirtyStorage[key]; dirty { + return value + } + return s.GetCommittedState(key) +} + +// SetState sets the contract state +func (s *stateObject) SetState(key common.Hash, value common.Hash) { + // If the new value is the same as old, don't set + prev := s.GetState(key) + if prev == value { + return + } + // New value is different, update and journal the change + s.db.Journal.append(storageChange{ + account: &s.address, + key: key, + prevalue: prev, + }) + s.setState(key, value) +} + +func (s *stateObject) setState(key, value common.Hash) { + s.DirtyStorage[key] = value +} diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go new file mode 100644 index 000000000..f711c6cb3 --- /dev/null +++ b/x/evm/statedb/statedb.go @@ -0,0 +1,620 @@ +// The "evm/statedb" package implements a go-ethereum [vm.StateDB] with state +// management and journal changes specific to the Nibiru EVM. +// +// This package plays a critical role in managing the state of accounts, +// contracts, and storage while handling atomicity, caching, and state +// modifications. It ensures that state transitions made during the +// execution of smart contracts are either committed or reverted based +// on transaction outcomes. +// +// StateDB structs used to store anything within the state tree, including +// accounts, contracts, and contract storage. +// Note that Nibiru's state tree is an IAVL tree, which differs from the Merkle +// Patricia Trie structure seen on Ethereum mainnet. +// +// StateDBs also take care of caching and handling nested states. +package statedb + +// Copyright (c) 2023-2024 Nibi, Inc. + +import ( + "fmt" + "math/big" + "sort" + + store "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" +) + +var _ vm.StateDB = &StateDB{} + +// StateDB structs within the ethereum protocol are used to store anything +// within the merkle trie. StateDBs take care of caching and storing +// nested states. It's the general query interface to retrieve: +// * Contracts +// * Accounts +type StateDB struct { + keeper Keeper + + // evmTxCtx is the persistent context used for official `StateDB.Commit` calls. + evmTxCtx sdk.Context + + // Journal of state modifications. This is the backbone of + // Snapshot and RevertToSnapshot. + Journal *journal + validRevisions []revision + nextRevisionID int + + stateObjects map[common.Address]*stateObject + + txConfig TxConfig + + // cacheCtx: An sdk.Context produced from the [StateDB.ctx] with the + // multi-store cached and a new event manager. The cached context + // (`cacheCtx`) is written to the persistent context (`ctx`) when + // `writeCacheCtx` is called. + cacheCtx sdk.Context + + // writeToCommitCtxFromCacheCtx is the "write" function received from + // `s.evmTxCtx.CacheContext()`. It saves mutations on s.cacheCtx to the StateDB's + // commit context (s.evmTxCtx). This synchronizes the multistore and event manager + // of the two contexts. + writeToCommitCtxFromCacheCtx func() + + // The number of precompiled contract calls within the current transaction + multistoreCacheCount uint8 + + // The refund counter, also used by state transitioning. + refund uint64 + + // Per-transaction logs + logs []*gethcore.Log + + // Per-transaction access list + accessList *accessList +} + +func FromVM(evmObj *vm.EVM) *StateDB { + return evmObj.StateDB.(*StateDB) +} + +// New creates a new state from a given trie. +func New(ctx sdk.Context, keeper Keeper, txConfig TxConfig) *StateDB { + return &StateDB{ + keeper: keeper, + evmTxCtx: ctx, + stateObjects: make(map[common.Address]*stateObject), + Journal: newJournal(), + accessList: newAccessList(), + + txConfig: txConfig, + } +} + +// revision is the identifier of a version of state. +// it consists of an auto-increment id and a journal index. +// it's safer to use than using journal index alone. +type revision struct { + id int + journalIndex int +} + +// Keeper returns the underlying `Keeper` +func (s *StateDB) Keeper() Keeper { + return s.keeper +} + +// GetEvmTxContext returns the EVM transaction context. +func (s *StateDB) GetEvmTxContext() sdk.Context { + return s.evmTxCtx +} + +// GetCacheContext: Getter for testing purposes. +func (s *StateDB) GetCacheContext() *sdk.Context { + if s.writeToCommitCtxFromCacheCtx == nil { + return nil + } + return &s.cacheCtx +} + +// AddLog adds to the EVM's event log for the current transaction. +// [AddLog] uses the [TxConfig] to populate the tx hash, block hash, tx index, +// and event log index. +func (s *StateDB) AddLog(log *gethcore.Log) { + s.Journal.append(addLogChange{}) + + log.TxHash = s.txConfig.TxHash + log.BlockHash = s.txConfig.BlockHash + log.TxIndex = s.txConfig.TxIndex + log.Index = s.txConfig.LogIndex + uint(len(s.logs)) + s.logs = append(s.logs, log) +} + +// Logs returns the event logs of current transaction. +func (s *StateDB) Logs() []*gethcore.Log { + return s.logs +} + +// AddRefund adds gas to the refund counter +func (s *StateDB) AddRefund(gas uint64) { + s.Journal.append(refundChange{prev: s.refund}) + s.refund += gas +} + +// SubRefund removes gas from the refund counter. +// This method will panic if the refund counter goes below zero +func (s *StateDB) SubRefund(gas uint64) { + s.Journal.append(refundChange{prev: s.refund}) + if gas > s.refund { + panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund)) + } + s.refund -= gas +} + +// Exist reports whether the given account address exists in the state. +// Notably this also returns true for suicided accounts. +func (s *StateDB) Exist(addr common.Address) bool { + return s.getStateObject(addr) != nil +} + +// Empty returns whether the state object is either non-existent +// or empty according to the EIP161 specification (balance = nonce = code = 0) +func (s *StateDB) Empty(addr common.Address) bool { + so := s.getStateObject(addr) + return so == nil || so.isEmpty() +} + +// GetBalance retrieves the balance from the given address or 0 if object not found +func (s *StateDB) GetBalance(addr common.Address) *big.Int { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Balance() + } + return common.Big0 +} + +// GetNonce returns the nonce of account, 0 if not exists. +func (s *StateDB) GetNonce(addr common.Address) uint64 { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Nonce() + } + + return 0 +} + +// GetCode returns the code of account, nil if not exists. +func (s *StateDB) GetCode(addr common.Address) []byte { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Code() + } + return nil +} + +// GetCodeSize returns the code size of account. +func (s *StateDB) GetCodeSize(addr common.Address) int { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.CodeSize() + } + return 0 +} + +// GetCodeHash returns the code hash of account. +func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject == nil { + return common.Hash{} + } + return common.BytesToHash(stateObject.CodeHash()) +} + +// GetState retrieves a value from the given account's storage trie. +func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.GetState(hash) + } + return common.Hash{} +} + +// GetCommittedState retrieves a value from the given account's committed storage trie. +func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.GetCommittedState(hash) + } + return common.Hash{} +} + +// GetRefund returns the current value of the refund counter. +func (s *StateDB) GetRefund() uint64 { + return s.refund +} + +// HasSuicided returns if the contract is suicided in current transaction. +func (s *StateDB) HasSuicided(addr common.Address) bool { + stateObject := s.getStateObject(addr) + if stateObject != nil { + return stateObject.Suicided + } + return false +} + +// AddPreimage records a SHA3 preimage seen by the VM. +// AddPreimage performs a no-op since the EnablePreimageRecording flag is disabled +// on the vm.Config during state transitions. No store trie preimages are written +// to the database. +func (s *StateDB) AddPreimage(_ common.Hash, _ []byte) {} + +// getStateObject retrieves a state object given by the address, returning nil if +// the object is not found. +func (s *StateDB) getStateObject(addr common.Address) *stateObject { + // Prefer live objects if any is available + if obj := s.stateObjects[addr]; obj != nil { + return obj + } + + // If no live objects are available, load it from keeper + ctx := s.evmTxCtx + if s.writeToCommitCtxFromCacheCtx != nil { + ctx = s.cacheCtx + } + account := s.keeper.GetAccount(ctx, addr) + if account == nil { + return nil + } + + // Insert into the live set + obj := newObject(s, addr, *account) + s.setStateObject(obj) + return obj +} + +// getOrNewStateObject retrieves a state object or create a new state object if nil. +func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject { + stateObject := s.getStateObject(addr) + if stateObject == nil { + stateObject, _ = s.createObject(addr) + } + return stateObject +} + +// createObject creates a new state object. If there is an existing account with +// the given address, it is overwritten and returned as the second return value. +func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { + prev = s.getStateObject(addr) + + newobj = newObject(s, addr, Account{}) + if prev == nil { + s.Journal.append(createObjectChange{account: &addr}) + } else { + s.Journal.append(resetObjectChange{prev: prev}) + } + s.setStateObject(newobj) + if prev != nil { + return newobj, prev + } + return newobj, nil +} + +// CreateAccount explicitly creates a state object. If a state object with the address +// already exists the balance is carried over to the new account. +// +// CreateAccount is called during the EVM CREATE operation. The situation might arise that +// a contract does the following: +// +// 1. sends funds to sha(account ++ (nonce + 1)) +// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) +// +// Carrying over the balance ensures that Ether doesn't disappear. +func (s *StateDB) CreateAccount(addr common.Address) { + newObj, prev := s.createObject(addr) + if prev != nil { + newObj.setBalance(prev.account.BalanceWei) + } +} + +// ForEachStorage iterate the contract storage, the iteration order is not defined. +func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { + so := s.getStateObject(addr) + if so == nil { + return nil + } + ctx := s.evmTxCtx + if s.writeToCommitCtxFromCacheCtx != nil { + ctx = s.cacheCtx + } + s.keeper.ForEachStorage(ctx, addr, func(key, value common.Hash) bool { + if value, dirty := so.DirtyStorage[key]; dirty { + return cb(key, value) + } + if len(value) > 0 { + return cb(key, value) + } + return true + }) + return nil +} + +func (s *StateDB) setStateObject(object *stateObject) { + s.stateObjects[object.Address()] = object +} + +/* + * SETTERS + */ + +// AddBalance adds amount to the account associated with addr. +func (s *StateDB) AddBalance(addr common.Address, wei *big.Int) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.AddBalance(wei) + } +} + +// SubBalance subtracts amount from the account associated with addr. +func (s *StateDB) SubBalance(addr common.Address, wei *big.Int) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SubBalance(wei) + } +} + +func (s *StateDB) SetBalanceWei(addr common.Address, wei *big.Int) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetBalance(wei) + } +} + +// SetNonce sets the nonce of account. +func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetNonce(nonce) + } +} + +// SetCode sets the code of account. +func (s *StateDB) SetCode(addr common.Address, code []byte) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetCode(crypto.Keccak256Hash(code), code) + } +} + +// SetState sets the contract state. +func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { + stateObject := s.getOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetState(key, value) + } +} + +// Suicide marks the given account as suicided. +// This clears the account balance. +// +// The account's state object is still available until the state is committed, +// getStateObject will return a non-nil account after Suicide. +func (s *StateDB) Suicide(addr common.Address) bool { + stateObject := s.getStateObject(addr) + if stateObject == nil { + return false + } + s.Journal.append(suicideChange{ + account: &addr, + prev: stateObject.Suicided, + prevbalance: new(big.Int).Set(stateObject.Balance()), + }) + stateObject.Suicided = true + stateObject.account.BalanceWei = new(big.Int) + + return true +} + +// PrepareAccessList handles the preparatory steps for executing a state transition with +// regards to both EIP-2929 and EIP-2930: +// +// - Add sender to access list (2929) +// - Add destination to access list (2929) +// - Add precompiles to access list (2929) +// - Add the contents of the optional tx access list (2930) +// +// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. +func (s *StateDB) PrepareAccessList( + sender common.Address, + dst *common.Address, + precompiles []common.Address, + list gethcore.AccessList, +) { + s.AddAddressToAccessList(sender) + if dst != nil { + s.AddAddressToAccessList(*dst) + // If it's a create-tx, the destination will be added inside evm.create + } + for _, addr := range precompiles { + s.AddAddressToAccessList(addr) + } + for _, el := range list { + s.AddAddressToAccessList(el.Address) + for _, key := range el.StorageKeys { + s.AddSlotToAccessList(el.Address, key) + } + } +} + +// AddAddressToAccessList adds the given address to the access list +func (s *StateDB) AddAddressToAccessList(addr common.Address) { + if s.accessList.AddAddress(addr) { + s.Journal.append(accessListAddAccountChange{&addr}) + } +} + +// AddSlotToAccessList adds the given (address, slot)-tuple to the access list +func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { + addrMod, slotMod := s.accessList.AddSlot(addr, slot) + if addrMod { + // In practice, this should not happen, since there is no way to enter the + // scope of 'address' without having the 'address' become already added + // to the access list (via call-variant, create, etc). + // Better safe than sorry, though + s.Journal.append(accessListAddAccountChange{&addr}) + } + if slotMod { + s.Journal.append(accessListAddSlotChange{ + address: &addr, + slot: &slot, + }) + } +} + +// AddressInAccessList returns true if the given address is in the access list. +func (s *StateDB) AddressInAccessList(addr common.Address) bool { + return s.accessList.ContainsAddress(addr) +} + +// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list. +func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { + return s.accessList.Contains(addr, slot) +} + +// Snapshot returns an identifier for the current revision of the state. +func (s *StateDB) Snapshot() int { + id := s.nextRevisionID + s.nextRevisionID++ + s.validRevisions = append(s.validRevisions, revision{id, s.Journal.Length()}) + return id +} + +// RevertToSnapshot reverts all state changes made since the given revision. +func (s *StateDB) RevertToSnapshot(revid int) { + // Find the snapshot in the stack of valid snapshots. + idx := sort.Search(len(s.validRevisions), func(i int) bool { + return s.validRevisions[i].id >= revid + }) + if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid { + panic(fmt.Errorf("revision id %v cannot be reverted", revid)) + } + snapshot := s.validRevisions[idx].journalIndex + + // Replay the journal to undo changes and remove invalidated snapshots + s.Journal.Revert(s, snapshot) + s.validRevisions = s.validRevisions[:idx] +} + +// errorf: wrapper of "fmt.Errorf" specific to the current Go package. +func errorf(format string, args ...any) error { + return fmt.Errorf("StateDB error: "+format, args...) +} + +// Commit writes the dirty journal state changes to the EVM Keeper. The +// StateDB object cannot be reused after [Commit] has completed. A new +// object needs to be created from the EVM. +// +// cacheCtxSyncNeeded: If one of the [Nibiru-Specific Precompiled Contracts] was +// called, a [JournalChange] of type [PrecompileSnapshotBeforeRun] gets added and +// we branch off a cache of the commit context (s.evmTxCtx). +// +// [Nibiru-Specific Precompiled Contracts]: https://nibiru.fi/docs/evm/precompiles/nibiru.html +func (s *StateDB) Commit() error { + if s.writeToCommitCtxFromCacheCtx != nil { + s.writeToCommitCtxFromCacheCtx() + } + return s.commitCtx(s.GetEvmTxContext()) +} + +// CommitCacheCtx is identical to [StateDB.Commit], except it: +// (1) uses the cacheCtx of the [StateDB] and +// (2) does not save mutations of the cacheCtx to the commit context (s.evmTxCtx). +// The reason for (2) is that the overall EVM transaction (block, not internal) +// is only finalized when [Commit] is called, not when [CommitCacheCtx] is +// called. +func (s *StateDB) CommitCacheCtx() error { + return s.commitCtx(s.cacheCtx) +} + +// commitCtx writes the dirty journal state changes to the EVM Keeper. The +// StateDB object cannot be reused after [commitCtx] has completed. A new +// object needs to be created from the EVM. +func (s *StateDB) commitCtx(ctx sdk.Context) error { + for _, addr := range s.Journal.sortedDirties() { + obj := s.getStateObject(addr) + if obj == nil { + s.Journal.dirties[addr] = 0 + continue + } + if obj.Suicided { + // Invariant: After [StateDB.Suicide] for some address, the + // corresponding account's state object is only available until the + // state is committed. + if err := s.keeper.DeleteAccount(ctx, obj.Address()); err != nil { + return errorf("failed to delete account: %w", err) + } + delete(s.stateObjects, addr) + } else { + if obj.code != nil && obj.DirtyCode { + s.keeper.SetCode(ctx, obj.CodeHash(), obj.code) + } + if err := s.keeper.SetAccount(ctx, obj.Address(), obj.account.ToNative()); err != nil { + return errorf("failed to set account: %w", err) + } + for _, key := range obj.DirtyStorage.SortedKeys() { + dirtyVal := obj.DirtyStorage[key] + // Values that match origin storage are not dirty. + if dirtyVal == obj.OriginStorage[key] { + continue + } + // Persist committed changes + s.keeper.SetState(ctx, obj.Address(), key, dirtyVal.Bytes()) + obj.OriginStorage[key] = dirtyVal + } + } + // TODO: UD-DEBUG: Assume clean to pretend for tests + // Reset the dirty count to 0 because all state changes for this dirtied + // address in the journal have been committed. + s.Journal.dirties[addr] = 0 + } + return nil +} + +func (s *StateDB) CacheCtxForPrecompile() ( + sdk.Context, PrecompileCalled, +) { + if s.writeToCommitCtxFromCacheCtx == nil { + s.cacheCtx, s.writeToCommitCtxFromCacheCtx = s.evmTxCtx.CacheContext() + } + return s.cacheCtx, PrecompileCalled{ + MultiStore: s.cacheCtx.MultiStore().(store.CacheMultiStore).Copy(), + Events: s.cacheCtx.EventManager().Events(), + } +} + +// SavePrecompileCalledJournalChange adds a snapshot of the commit multistore +// ([PrecompileCalled]) to the [StateDB] journal at the end of +// successful invocation of a precompiled contract. This is necessary to revert +// intermediate states where an EVM contract augments the multistore with a +// precompile and an inconsistency occurs between the EVM module and other +// modules. +// +// See [PrecompileCalled] for more info. +func (s *StateDB) SavePrecompileCalledJournalChange( + journalChange PrecompileCalled, +) error { + s.Journal.append(journalChange) + s.multistoreCacheCount++ + if s.multistoreCacheCount > maxMultistoreCacheCount { + return fmt.Errorf( + "exceeded maximum number Nibiru-specific precompiled contract calls in one transaction (%d).", + maxMultistoreCacheCount, + ) + } + return nil +} + +const maxMultistoreCacheCount uint8 = 10 diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go new file mode 100644 index 000000000..fc0a1fdc0 --- /dev/null +++ b/x/evm/statedb/statedb_test.go @@ -0,0 +1,627 @@ +package statedb_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/nibiru/v2/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +// emptyCodeHash: The hash for empty contract bytecode, or a blank byte +// array. This is the code hash for a non-existent or empty account. +var emptyCodeHash []byte = crypto.Keccak256(nil) + +// dummy variables for tests +var ( + address common.Address = common.BigToAddress(big.NewInt(101)) + address2 common.Address = common.BigToAddress(big.NewInt(102)) + address3 common.Address = common.BigToAddress(big.NewInt(103)) + blockHash common.Hash = common.BigToHash(big.NewInt(9999)) + errAddress common.Address = common.BigToAddress(big.NewInt(100)) +) + +// TestSuite runs the entire test suite. +func TestSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} + +type Suite struct { + suite.Suite +} + +// CollectContractStorage is a helper function that collects all storage key-value pairs +// for a given contract address using the ForEachStorage method of the StateDB. +// It returns a map of storage slots to their values. +func CollectContractStorage(db vm.StateDB) statedb.Storage { + storage := make(statedb.Storage) + err := db.ForEachStorage( + address, + func(k, v common.Hash) bool { + storage[k] = v + return true + }, + ) + if err != nil { + return nil + } + + return storage +} + +func (s *Suite) TestAccount() { + key1 := common.BigToHash(big.NewInt(1)) + value1 := common.BigToHash(big.NewInt(2)) + key2 := common.BigToHash(big.NewInt(3)) + value2 := common.BigToHash(big.NewInt(4)) + testCases := []struct { + name string + malleate func(deps *evmtest.TestDeps, db *statedb.StateDB) + }{ + {"non-exist account", func(deps *evmtest.TestDeps, db *statedb.StateDB) { + s.Require().Equal(false, db.Exist(address)) + s.Require().Equal(true, db.Empty(address)) + s.Require().Equal(big.NewInt(0), db.GetBalance(address)) + s.Require().Equal([]byte(nil), db.GetCode(address)) + s.Require().Equal(common.Hash{}, db.GetCodeHash(address)) + s.Require().Equal(uint64(0), db.GetNonce(address)) + }}, + {"empty account", func(deps *evmtest.TestDeps, db *statedb.StateDB) { + db.CreateAccount(address) + s.Require().NoError(db.Commit()) + + k := db.Keeper() + acct := k.GetAccount(deps.Ctx, address) + s.Require().EqualValues(statedb.NewEmptyAccount(), acct) + s.Require().Empty(CollectContractStorage(db)) + + db = deps.NewStateDB() + s.Require().Equal(true, db.Exist(address)) + s.Require().Equal(true, db.Empty(address)) + s.Require().Equal(big.NewInt(0), db.GetBalance(address)) + s.Require().Equal([]byte(nil), db.GetCode(address)) + s.Require().Equal(common.BytesToHash(emptyCodeHash), db.GetCodeHash(address)) + s.Require().Equal(uint64(0), db.GetNonce(address)) + }}, + {"suicide", func(deps *evmtest.TestDeps, db *statedb.StateDB) { + // non-exist account. + s.Require().False(db.Suicide(address)) + s.Require().False(db.HasSuicided(address)) + + // create a contract account + db.CreateAccount(address) + db.SetCode(address, []byte("hello world")) + db.AddBalance(address, big.NewInt(100)) + db.SetState(address, key1, value1) + db.SetState(address, key2, value2) + s.Require().NoError(db.Commit()) + + // suicide + db = deps.NewStateDB() + s.Require().False(db.HasSuicided(address)) + s.Require().True(db.Suicide(address)) + + // check dirty state + s.Require().True(db.HasSuicided(address)) + // balance is cleared + s.Require().Equal(big.NewInt(0), db.GetBalance(address)) + // but code and state are still accessible in dirty state + s.Require().Equal(value1, db.GetState(address, key1)) + s.Require().Equal([]byte("hello world"), db.GetCode(address)) + + s.Require().NoError(db.Commit()) + + // not accessible from StateDB anymore + db = deps.NewStateDB() + s.Require().False(db.Exist(address)) + s.Require().Empty(CollectContractStorage(db)) + }}, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + tc.malleate(&deps, db) + }) + } +} + +func (s *Suite) TestAccountOverride() { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + // test balance carry over when overwritten + amount := big.NewInt(1) + + // init an EOA account, account overridden only happens on EOA account. + db.AddBalance(address, amount) + db.SetNonce(address, 1) + + // override + db.CreateAccount(address) + + // check balance is not lost + s.Require().Equal(amount, db.GetBalance(address)) + // but nonce is reset + s.Require().Equal(uint64(0), db.GetNonce(address)) +} + +func (s *Suite) TestDBError() { + testCases := []struct { + name string + malleate func(vm.StateDB) + }{ + {"set account", func(db vm.StateDB) { + db.SetNonce(errAddress, 1) + }}, + {"delete account", func(db vm.StateDB) { + db.SetNonce(errAddress, 1) + s.Require().True(db.Suicide(errAddress)) + s.True(db.HasSuicided(errAddress)) + }}, + } + for _, tc := range testCases { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + tc.malleate(db) + s.Require().NoError(db.Commit()) + } +} + +func (s *Suite) TestBalance() { + // NOTE: no need to test overflow/underflow, that is guaranteed by evm implementation. + testCases := []struct { + name string + malleate func(*statedb.StateDB) + expBalance *big.Int + }{ + {"add balance", func(db *statedb.StateDB) { + db.AddBalance(address, big.NewInt(10)) + }, big.NewInt(10)}, + {"sub balance", func(db *statedb.StateDB) { + db.AddBalance(address, big.NewInt(10)) + // get dirty balance + s.Require().Equal(big.NewInt(10), db.GetBalance(address)) + db.SubBalance(address, big.NewInt(2)) + }, big.NewInt(8)}, + {"add zero balance", func(db *statedb.StateDB) { + db.AddBalance(address, big.NewInt(0)) + }, big.NewInt(0)}, + {"sub zero balance", func(db *statedb.StateDB) { + db.SubBalance(address, big.NewInt(0)) + }, big.NewInt(0)}, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + tc.malleate(db) + + // check dirty state + s.Require().Equal(tc.expBalance, db.GetBalance(address)) + s.Require().NoError(db.Commit()) + + // check committed balance too + s.Require().Equal(tc.expBalance, db.GetBalance(address)) + }) + } +} + +func (s *Suite) TestState() { + key1 := common.BigToHash(big.NewInt(1)) + value1 := common.BigToHash(big.NewInt(1)) + testCases := []struct { + name string + malleate func(*statedb.StateDB) + expStates statedb.Storage + }{ + {"empty state", func(db *statedb.StateDB) { + }, nil}, + {"set empty value", func(db *statedb.StateDB) { + db.SetState(address, key1, common.Hash{}) + }, statedb.Storage{}}, + {"noop state change", func(db *statedb.StateDB) { + db.SetState(address, key1, value1) + db.SetState(address, key1, common.Hash{}) + }, statedb.Storage{}}, + {"set state", func(db *statedb.StateDB) { + // check empty initial state + s.Require().Equal(common.Hash{}, db.GetState(address, key1)) + s.Require().Equal(common.Hash{}, db.GetCommittedState(address, key1)) + + // set state + db.SetState(address, key1, value1) + // query dirty state + s.Require().Equal(value1, db.GetState(address, key1)) + // check committed state is still not exist + s.Require().Equal(common.Hash{}, db.GetCommittedState(address, key1)) + + // set same value again, should be noop + db.SetState(address, key1, value1) + s.Require().Equal(value1, db.GetState(address, key1)) + }, statedb.Storage{ + key1: value1, + }}, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + tc.malleate(db) + s.Require().NoError(db.Commit()) + + // check committed states in keeper + for k, v := range tc.expStates { + s.Equal(v, db.GetState(address, k)) + } + + // check ForEachStorage + db = deps.NewStateDB() + collected := CollectContractStorage(db) + if len(tc.expStates) > 0 { + s.Require().Equal(tc.expStates, collected) + } else { + s.Require().Empty(collected) + } + }) + } +} + +func (s *Suite) TestCode() { + code := []byte("hello world") + codeHash := crypto.Keccak256Hash(code) + + testCases := []struct { + name string + malleate func(vm.StateDB) + expCode []byte + expCodeHash common.Hash + }{ + {"non-exist account", func(vm.StateDB) {}, nil, common.Hash{}}, + {"empty account", func(db vm.StateDB) { + db.CreateAccount(address) + }, nil, common.BytesToHash(emptyCodeHash)}, + {"set code", func(db vm.StateDB) { + db.SetCode(address, code) + }, code, codeHash}, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + tc.malleate(db) + + // check dirty state + s.Require().Equal(tc.expCode, db.GetCode(address)) + s.Require().Equal(len(tc.expCode), db.GetCodeSize(address)) + s.Require().Equal(tc.expCodeHash, db.GetCodeHash(address)) + + s.Require().NoError(db.Commit()) + + // check again + db = deps.NewStateDB() + s.Require().Equal(tc.expCode, db.GetCode(address)) + s.Require().Equal(len(tc.expCode), db.GetCodeSize(address)) + s.Require().Equal(tc.expCodeHash, db.GetCodeHash(address)) + }) + } +} + +func (s *Suite) TestRevertSnapshot() { + v1 := common.BigToHash(big.NewInt(1)) + v2 := common.BigToHash(big.NewInt(2)) + v3 := common.BigToHash(big.NewInt(3)) + testCases := []struct { + name string + malleate func(vm.StateDB) + }{ + {"set state", func(db vm.StateDB) { + db.SetState(address, v1, v3) + }}, + {"set nonce", func(db vm.StateDB) { + db.SetNonce(address, 10) + }}, + {"change balance", func(db vm.StateDB) { + db.AddBalance(address, big.NewInt(10)) + db.SubBalance(address, big.NewInt(5)) + }}, + {"override account", func(db vm.StateDB) { + db.CreateAccount(address) + }}, + {"set code", func(db vm.StateDB) { + db.SetCode(address, []byte("hello world")) + }}, + {"suicide", func(db vm.StateDB) { + db.SetState(address, v1, v2) + db.SetCode(address, []byte("hello world")) + s.Require().True(db.Suicide(address)) + }}, + {"add log", func(db vm.StateDB) { + db.AddLog(&gethcore.Log{ + Address: address, + }) + }}, + {"add refund", func(db vm.StateDB) { + db.AddRefund(10) + db.SubRefund(5) + }}, + {"access list", func(db vm.StateDB) { + db.AddAddressToAccessList(address) + db.AddSlotToAccessList(address, v1) + }}, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + + // do some arbitrary changes to the storage + db := deps.NewStateDB() + db.SetNonce(address, 1) + db.AddBalance(address, big.NewInt(100)) + db.SetCode(address, []byte("hello world")) + db.SetState(address, v1, v2) + db.SetNonce(address2, 1) + s.Require().NoError(db.Commit()) + + // Store original state values + originalNonce := db.GetNonce(address) + originalBalance := db.GetBalance(address) + originalCode := db.GetCode(address) + originalState := db.GetState(address, v1) + originalNonce2 := db.GetNonce(address2) + + // run test + rev := db.Snapshot() + tc.malleate(db) + db.RevertToSnapshot(rev) + + // check empty states after revert + s.Require().Zero(db.GetRefund()) + s.Require().Empty(db.Logs()) + + s.Require().NoError(db.Commit()) + + // Check again after commit to ensure persistence + s.Require().Equal(originalNonce, db.GetNonce(address)) + s.Require().Equal(originalBalance, db.GetBalance(address)) + s.Require().Equal(originalCode, db.GetCode(address)) + s.Require().Equal(originalState, db.GetState(address, v1)) + s.Require().Equal(originalNonce2, db.GetNonce(address2)) + }) + } +} + +func (s *Suite) TestNestedSnapshot() { + key := common.BigToHash(big.NewInt(1)) + value1 := common.BigToHash(big.NewInt(1)) + value2 := common.BigToHash(big.NewInt(2)) + + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + + rev1 := db.Snapshot() + db.SetState(address, key, value1) + + rev2 := db.Snapshot() + db.SetState(address, key, value2) + s.Require().Equal(value2, db.GetState(address, key)) + + db.RevertToSnapshot(rev2) + s.Require().Equal(value1, db.GetState(address, key)) + + db.RevertToSnapshot(rev1) + s.Require().Equal(common.Hash{}, db.GetState(address, key)) +} + +func (s *Suite) TestInvalidSnapshotId() { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + + s.Require().Panics(func() { + db.RevertToSnapshot(1) + }) +} + +func (s *Suite) TestAccessList() { + value1 := common.BigToHash(big.NewInt(1)) + value2 := common.BigToHash(big.NewInt(2)) + + testCases := []struct { + name string + malleate func(vm.StateDB) + }{ + {"add address", func(db vm.StateDB) { + s.Require().False(db.AddressInAccessList(address)) + db.AddAddressToAccessList(address) + s.Require().True(db.AddressInAccessList(address)) + + addrPresent, slotPresent := db.SlotInAccessList(address, value1) + s.Require().True(addrPresent) + s.Require().False(slotPresent) + + // add again, should be no-op + db.AddAddressToAccessList(address) + s.Require().True(db.AddressInAccessList(address)) + }}, + {"add slot", func(db vm.StateDB) { + addrPresent, slotPresent := db.SlotInAccessList(address, value1) + s.Require().False(addrPresent) + s.Require().False(slotPresent) + db.AddSlotToAccessList(address, value1) + addrPresent, slotPresent = db.SlotInAccessList(address, value1) + s.Require().True(addrPresent) + s.Require().True(slotPresent) + + // add another slot + db.AddSlotToAccessList(address, value2) + addrPresent, slotPresent = db.SlotInAccessList(address, value2) + s.Require().True(addrPresent) + s.Require().True(slotPresent) + + // add again, should be noop + db.AddSlotToAccessList(address, value2) + addrPresent, slotPresent = db.SlotInAccessList(address, value2) + s.Require().True(addrPresent) + s.Require().True(slotPresent) + }}, + {"prepare access list", func(db vm.StateDB) { + al := gethcore.AccessList{{ + Address: address3, + StorageKeys: []common.Hash{value1}, + }} + + db.PrepareAccessList(address, &address2, vm.PrecompiledAddressesBerlin, al) + + // check sender and dst + s.Require().True(db.AddressInAccessList(address)) + s.Require().True(db.AddressInAccessList(address2)) + // check precompiles + s.Require().True(db.AddressInAccessList(common.BytesToAddress([]byte{1}))) + // check AccessList + s.Require().True(db.AddressInAccessList(address3)) + addrPresent, slotPresent := db.SlotInAccessList(address3, value1) + s.Require().True(addrPresent) + s.Require().True(slotPresent) + addrPresent, slotPresent = db.SlotInAccessList(address3, value2) + s.Require().True(addrPresent) + s.Require().False(slotPresent) + }}, + } + + for _, tc := range testCases { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + tc.malleate(db) + } +} + +func (s *Suite) TestLog() { + txHash := common.BytesToHash([]byte("tx")) + + // use a non-default tx config + const ( + blockNumber = uint64(1) + txIdx = uint(1) + logIdx = uint(1) + ) + txConfig := statedb.TxConfig{ + BlockHash: blockHash, + TxHash: txHash, + TxIndex: txIdx, + LogIndex: logIdx, + } + + deps := evmtest.NewTestDeps() + db := statedb.New(deps.Ctx, deps.App.EvmKeeper, txConfig) + + logData := []byte("hello world") + log := &gethcore.Log{ + Address: address, + Topics: []common.Hash{}, + Data: logData, + BlockNumber: blockNumber, + } + db.AddLog(log) + s.Require().Equal(1, len(db.Logs())) + + wantLog := &gethcore.Log{ + Address: log.Address, + Topics: log.Topics, + Data: log.Data, + BlockNumber: log.BlockNumber, + + // New fields + BlockHash: blockHash, + TxHash: txHash, + TxIndex: txIdx, + Index: logIdx, + } + s.Require().Equal(wantLog, db.Logs()[0]) + + // Add a second log and assert values + db.AddLog(log) + wantLog.Index++ + s.Require().Equal(2, len(db.Logs())) + gotLog := db.Logs()[1] + s.Require().Equal(wantLog, gotLog) +} + +func (s *Suite) TestRefund() { + testCases := []struct { + name string + malleate func(vm.StateDB) + expRefund uint64 + expPanic bool + }{ + {"add refund", func(db vm.StateDB) { + db.AddRefund(uint64(10)) + }, 10, false}, + {"sub refund", func(db vm.StateDB) { + db.AddRefund(uint64(10)) + db.SubRefund(uint64(5)) + }, 5, false}, + {"negative refund counter", func(db vm.StateDB) { + db.AddRefund(uint64(5)) + db.SubRefund(uint64(10)) + }, 0, true}, + } + for _, tc := range testCases { + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + if !tc.expPanic { + tc.malleate(db) + s.Require().Equal(tc.expRefund, db.GetRefund()) + } else { + s.Require().Panics(func() { + tc.malleate(db) + }) + } + } +} + +func (s *Suite) TestIterateStorage() { + key1 := common.BigToHash(big.NewInt(1)) + value1 := common.BigToHash(big.NewInt(2)) + key2 := common.BigToHash(big.NewInt(3)) + value2 := common.BigToHash(big.NewInt(4)) + + deps := evmtest.NewTestDeps() + db := deps.NewStateDB() + db.SetState(address, key1, value1) + db.SetState(address, key2, value2) + + // ForEachStorage only iterate committed state + s.Require().Empty(CollectContractStorage(db)) + + s.Require().NoError(db.Commit()) + + storage := CollectContractStorage(db) + s.Require().Equal(2, len(storage)) + + keySet := set.New[common.Hash](key1, key2) + valSet := set.New[common.Hash](value1, value2) + for _, stateKey := range storage.SortedKeys() { + stateValue := deps.EvmKeeper.GetState(deps.Ctx, address, stateKey) + s.True(keySet.Has(stateKey)) + s.True(valSet.Has(stateValue)) + } + + // break early iteration + storage = make(statedb.Storage) + err := db.ForEachStorage(address, func(k, v common.Hash) bool { + storage[k] = v + // return false to break early + return false + }) + s.Require().NoError(err) + s.Require().Equal(1, len(storage)) +} diff --git a/x/evm/tx.go b/x/evm/tx.go new file mode 100644 index 000000000..6c58c0f9c --- /dev/null +++ b/x/evm/tx.go @@ -0,0 +1,80 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "math" + "math/big" + + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// EvmTxArgs encapsulates all possible params to create all EVM txs types. +// This includes LegacyTx, DynamicFeeTx and AccessListTx +// TODO(k-yang): replace with JsonTxArgs. +type EvmTxArgs struct { + Nonce uint64 + GasLimit uint64 + Input []byte + GasFeeCap *big.Int + GasPrice *big.Int + ChainID *big.Int + Amount *big.Int + GasTipCap *big.Int + To *common.Address + Accesses *gethcore.AccessList +} + +// DefaultPriorityReduction is the default amount of price values required for 1 unit of priority. +// Because priority is `int64` while price is `big.Int`, it's necessary to scale down the range to keep it more pratical. +// The default value is the same as the `sdk.DefaultPowerReduction`. +var DefaultPriorityReduction = sdk.DefaultPowerReduction + +// GetTxPriority returns the priority of a given Ethereum tx. It relies on the +// priority reduction global variable to calculate the tx priority given the tx +// tip price: +// +// tx_priority = tip_price / priority_reduction +func GetTxPriority(txData TxData, baseFee *big.Int) (priority int64) { + // calculate priority based on effective gas price + tipPrice := txData.EffectiveGasPriceWeiPerGas(baseFee) + + // Return the min of the max possible priorty and the derived priority + priority = math.MaxInt64 + derivedPriority := new(big.Int).Quo(tipPrice, DefaultPriorityReduction.BigInt()) + + // Overflow safety check + var priorityBigI64 int64 + if derivedPriority.IsInt64() { + priorityBigI64 = derivedPriority.Int64() + } else { + priorityBigI64 = priority + } + return min(priority, priorityBigI64) +} + +// Failed returns if the contract execution failed in vm errors +func (m *MsgEthereumTxResponse) Failed() bool { + return len(m.VmError) > 0 +} + +// Return is a helper function to help caller distinguish between revert reason +// and function return. Return returns the data after execution if no error occurs. +func (m *MsgEthereumTxResponse) Return() []byte { + if m.Failed() { + return nil + } + return common.CopyBytes(m.Ret) +} + +// Revert returns the concrete revert reason if the execution is aborted by `REVERT` +// opcode. Note the reason can be nil if no data supplied with revert opcode. +func (m *MsgEthereumTxResponse) Revert() []byte { + if m.VmError != vm.ErrExecutionReverted.Error() { + return nil + } + return common.CopyBytes(m.Ret) +} diff --git a/x/evm/tx.pb.go b/x/evm/tx.pb.go new file mode 100644 index 000000000..29b728e61 --- /dev/null +++ b/x/evm/tx.pb.go @@ -0,0 +1,4247 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: eth/evm/v1/tx.proto + +package evm + +import ( + context "context" + cosmossdk_io_math "cosmossdk.io/math" + encoding_binary "encoding/binary" + fmt "fmt" + github_com_NibiruChain_nibiru_v2_eth "github.com/NibiruChain/nibiru/v2/eth" + _ "github.com/cosmos/cosmos-proto" + types "github.com/cosmos/cosmos-sdk/codec/types" + types1 "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgEthereumTx encapsulates an Ethereum transaction as an SDK message. +type MsgEthereumTx struct { + // data is inner transaction data of the Ethereum transaction + Data *types.Any `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + // size is the encoded storage size of the transaction (DEPRECATED) + Size_ float64 `protobuf:"fixed64,2,opt,name=size,proto3" json:"-"` + // hash of the transaction in hex format + Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty" rlp:"-"` + // from is the ethereum signer address in hex format. This address value is + // checked against the address derived from the signature (V, R, S) using the + // secp256k1 elliptic curve + From string `protobuf:"bytes,4,opt,name=from,proto3" json:"from,omitempty"` +} + +func (m *MsgEthereumTx) Reset() { *m = MsgEthereumTx{} } +func (m *MsgEthereumTx) String() string { return proto.CompactTextString(m) } +func (*MsgEthereumTx) ProtoMessage() {} +func (*MsgEthereumTx) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{0} +} +func (m *MsgEthereumTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgEthereumTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgEthereumTx.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgEthereumTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgEthereumTx.Merge(m, src) +} +func (m *MsgEthereumTx) XXX_Size() int { + return m.Size() +} +func (m *MsgEthereumTx) XXX_DiscardUnknown() { + xxx_messageInfo_MsgEthereumTx.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgEthereumTx proto.InternalMessageInfo + +// LegacyTx is the transaction data of regular Ethereum transactions. +// +// Note that setting "evm.Params.AllowUnprotectedTxs" to false will cause all +// non-EIP155 signed transactions to fail, as they'll lack replay protection. +// +// LegacyTx is a custom implementation of "LegacyTx" from +// "github.com/ethereum/go-ethereum/core/types". +type LegacyTx struct { + // nonce corresponds to the account nonce (transaction sequence). + Nonce uint64 `protobuf:"varint,1,opt,name=nonce,proto3" json:"nonce,omitempty"` + // gas_price defines the value for each gas unit + GasPrice *cosmossdk_io_math.Int `protobuf:"bytes,2,opt,name=gas_price,json=gasPrice,proto3,customtype=cosmossdk.io/math.Int" json:"gas_price,omitempty"` + // gas defines the gas limit defined for the transaction. + GasLimit uint64 `protobuf:"varint,3,opt,name=gas,proto3" json:"gas,omitempty"` + // to is the hex formatted address of the recipient + To string `protobuf:"bytes,4,opt,name=to,proto3" json:"to,omitempty"` + // value defines the unsigned integer value of the transaction amount. + Amount *cosmossdk_io_math.Int `protobuf:"bytes,5,opt,name=value,proto3,customtype=cosmossdk.io/math.Int" json:"value,omitempty"` + // data is the data payload bytes of the transaction. + Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"` + // v defines the recovery id as the "v" signature value from the elliptic + // curve digital signatute algorithm (ECDSA). It indicates which of two + // possible solutions should be used to reconstruct the public key from the + // signature. In Ethereum, "v" takes the value 27 or 28 for transactions that + // are not relay-protected. + V []byte `protobuf:"bytes,7,opt,name=v,proto3" json:"v,omitempty"` + // r defines the x-coordinate of a point on the elliptic curve in the elliptic + // curve digital signatute algorithm (ECDSA). It's crucial in ensuring + // uniqueness of the signature. + R []byte `protobuf:"bytes,8,opt,name=r,proto3" json:"r,omitempty"` + // s define the signature value derived from the private key, message hash, + // and the value of "r". It ensures that the signature is tied to both the + // message and the private key of the sender. + S []byte `protobuf:"bytes,9,opt,name=s,proto3" json:"s,omitempty"` +} + +func (m *LegacyTx) Reset() { *m = LegacyTx{} } +func (m *LegacyTx) String() string { return proto.CompactTextString(m) } +func (*LegacyTx) ProtoMessage() {} +func (*LegacyTx) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{1} +} +func (m *LegacyTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LegacyTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LegacyTx.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LegacyTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_LegacyTx.Merge(m, src) +} +func (m *LegacyTx) XXX_Size() int { + return m.Size() +} +func (m *LegacyTx) XXX_DiscardUnknown() { + xxx_messageInfo_LegacyTx.DiscardUnknown(m) +} + +var xxx_messageInfo_LegacyTx proto.InternalMessageInfo + +// AccessListTx is the data of EIP-2930 access list transactions. +// It is a custom implementation of "AccessListTx" from +// "github.com/ethereum/go-ethereum/core/types". +type AccessListTx struct { + // chain_id of the destination EVM chain + ChainID *cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3,customtype=cosmossdk.io/math.Int" json:"chainID"` + // nonce corresponds to the account nonce (transaction sequence). + Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"` + // gas_price defines the value for each gas unit + GasPrice *cosmossdk_io_math.Int `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice,proto3,customtype=cosmossdk.io/math.Int" json:"gas_price,omitempty"` + // gas defines the gas limit defined for the transaction. + GasLimit uint64 `protobuf:"varint,4,opt,name=gas,proto3" json:"gas,omitempty"` + // to is the recipient address in hex format + To string `protobuf:"bytes,5,opt,name=to,proto3" json:"to,omitempty"` + // value defines the unsigned integer value of the transaction amount. + Amount *cosmossdk_io_math.Int `protobuf:"bytes,6,opt,name=value,proto3,customtype=cosmossdk.io/math.Int" json:"value,omitempty"` + // data is the data payload bytes of the transaction. + Data []byte `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"` + // accesses is an array of access tuples + Accesses AccessList `protobuf:"bytes,8,rep,name=accesses,proto3,castrepeated=AccessList" json:"accessList"` + // v defines the recovery id and "v" signature value from the elliptic curve + // digital signatute algorithm (ECDSA). It indicates which of two possible + // solutions should be used to reconstruct the public key from the signature. + // In Ethereum, "v" takes the value 27 or 28 for transactions that are not + // relay-protected. + V []byte `protobuf:"bytes,9,opt,name=v,proto3" json:"v,omitempty"` + // r defines the x-coordinate of a point on the elliptic curve in the elliptic + // curve digital signatute algorithm (ECDSA). It's crucial in ensuring + // uniqueness of the signature. + R []byte `protobuf:"bytes,10,opt,name=r,proto3" json:"r,omitempty"` + // s define the signature value derived from the private key, message hash, + // and the value of "r". It ensures that the signature is tied to both the + // message and the private key of the sender. + S []byte `protobuf:"bytes,11,opt,name=s,proto3" json:"s,omitempty"` +} + +func (m *AccessListTx) Reset() { *m = AccessListTx{} } +func (m *AccessListTx) String() string { return proto.CompactTextString(m) } +func (*AccessListTx) ProtoMessage() {} +func (*AccessListTx) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{2} +} +func (m *AccessListTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AccessListTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AccessListTx.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AccessListTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_AccessListTx.Merge(m, src) +} +func (m *AccessListTx) XXX_Size() int { + return m.Size() +} +func (m *AccessListTx) XXX_DiscardUnknown() { + xxx_messageInfo_AccessListTx.DiscardUnknown(m) +} + +var xxx_messageInfo_AccessListTx proto.InternalMessageInfo + +// DynamicFeeTx is the data of EIP-1559 dynamic fee transactions. It is a custom +// implementation of "DynamicFeeTx" from +// "github.com/ethereum/go-ethereum/core/types". +type DynamicFeeTx struct { + // chain_id of the destination EVM chain + ChainID *cosmossdk_io_math.Int `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3,customtype=cosmossdk.io/math.Int" json:"chainID"` + // nonce corresponds to the account nonce (transaction sequence). + Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"` + // gas_tip_cap defines the max value for the gas tip + GasTipCap *cosmossdk_io_math.Int `protobuf:"bytes,3,opt,name=gas_tip_cap,json=gasTipCap,proto3,customtype=cosmossdk.io/math.Int" json:"gas_tip_cap,omitempty"` + // gas_fee_cap defines the max value for the gas fee + GasFeeCap *cosmossdk_io_math.Int `protobuf:"bytes,4,opt,name=gas_fee_cap,json=gasFeeCap,proto3,customtype=cosmossdk.io/math.Int" json:"gas_fee_cap,omitempty"` + // gas defines the gas limit defined for the transaction. + GasLimit uint64 `protobuf:"varint,5,opt,name=gas,proto3" json:"gas,omitempty"` + // to is the hex formatted address of the recipient + To string `protobuf:"bytes,6,opt,name=to,proto3" json:"to,omitempty"` + // value defines the the transaction amount. + Amount *cosmossdk_io_math.Int `protobuf:"bytes,7,opt,name=value,proto3,customtype=cosmossdk.io/math.Int" json:"value,omitempty"` + // data is the data payload bytes of the transaction. + Data []byte `protobuf:"bytes,8,opt,name=data,proto3" json:"data,omitempty"` + // accesses is an array of access tuples + Accesses AccessList `protobuf:"bytes,9,rep,name=accesses,proto3,castrepeated=AccessList" json:"accessList"` + // v defines the recovery id and "v" signature value from the elliptic curve + // digital signatute algorithm (ECDSA). It indicates which of two possible + // solutions should be used to reconstruct the public key from the signature. + // In Ethereum, "v" takes the value 27 or 28 for transactions that are not + // relay-protected. + V []byte `protobuf:"bytes,10,opt,name=v,proto3" json:"v,omitempty"` + // r defines the x-coordinate of a point on the elliptic curve in the elliptic + // curve digital signatute algorithm (ECDSA). It's crucial in ensuring + // uniqueness of the signature. + R []byte `protobuf:"bytes,11,opt,name=r,proto3" json:"r,omitempty"` + // s define the signature value derived from the private key, message hash, + // and the value of "r". It ensures that the signature is tied to both the + // message and the private key of the sender. + S []byte `protobuf:"bytes,12,opt,name=s,proto3" json:"s,omitempty"` +} + +func (m *DynamicFeeTx) Reset() { *m = DynamicFeeTx{} } +func (m *DynamicFeeTx) String() string { return proto.CompactTextString(m) } +func (*DynamicFeeTx) ProtoMessage() {} +func (*DynamicFeeTx) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{3} +} +func (m *DynamicFeeTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DynamicFeeTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DynamicFeeTx.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DynamicFeeTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_DynamicFeeTx.Merge(m, src) +} +func (m *DynamicFeeTx) XXX_Size() int { + return m.Size() +} +func (m *DynamicFeeTx) XXX_DiscardUnknown() { + xxx_messageInfo_DynamicFeeTx.DiscardUnknown(m) +} + +var xxx_messageInfo_DynamicFeeTx proto.InternalMessageInfo + +// ExtensionOptionsEthereumTx is an extension option for ethereum transactions +type ExtensionOptionsEthereumTx struct { +} + +func (m *ExtensionOptionsEthereumTx) Reset() { *m = ExtensionOptionsEthereumTx{} } +func (m *ExtensionOptionsEthereumTx) String() string { return proto.CompactTextString(m) } +func (*ExtensionOptionsEthereumTx) ProtoMessage() {} +func (*ExtensionOptionsEthereumTx) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{4} +} +func (m *ExtensionOptionsEthereumTx) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExtensionOptionsEthereumTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExtensionOptionsEthereumTx.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExtensionOptionsEthereumTx) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExtensionOptionsEthereumTx.Merge(m, src) +} +func (m *ExtensionOptionsEthereumTx) XXX_Size() int { + return m.Size() +} +func (m *ExtensionOptionsEthereumTx) XXX_DiscardUnknown() { + xxx_messageInfo_ExtensionOptionsEthereumTx.DiscardUnknown(m) +} + +var xxx_messageInfo_ExtensionOptionsEthereumTx proto.InternalMessageInfo + +// MsgEthereumTxResponse defines the Msg/EthereumTx response type. +type MsgEthereumTxResponse struct { + // hash of the ethereum transaction in hex format. This hash differs from the + // Tendermint sha256 hash of the transaction bytes. See + // https://github.com/tendermint/tendermint/issues/6539 for reference + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + // logs contains the transaction hash and the proto-compatible ethereum + // logs. + Logs []Log `protobuf:"bytes,2,rep,name=logs,proto3" json:"logs"` + // ret is the returned data from evm function (result or data supplied with + // revert opcode) + Ret []byte `protobuf:"bytes,3,opt,name=ret,proto3" json:"ret,omitempty"` + // vm_error is the error returned by vm execution + VmError string `protobuf:"bytes,4,opt,name=vm_error,json=vmError,proto3" json:"vm_error,omitempty"` + // gas_used specifies how much gas was consumed by the transaction + GasUsed uint64 `protobuf:"varint,5,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` +} + +func (m *MsgEthereumTxResponse) Reset() { *m = MsgEthereumTxResponse{} } +func (m *MsgEthereumTxResponse) String() string { return proto.CompactTextString(m) } +func (*MsgEthereumTxResponse) ProtoMessage() {} +func (*MsgEthereumTxResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{5} +} +func (m *MsgEthereumTxResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgEthereumTxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgEthereumTxResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgEthereumTxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgEthereumTxResponse.Merge(m, src) +} +func (m *MsgEthereumTxResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgEthereumTxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgEthereumTxResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgEthereumTxResponse proto.InternalMessageInfo + +// MsgUpdateParams defines a Msg for updating the x/evm module parameters. +type MsgUpdateParams struct { + // authority is the address of the governance account. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the x/evm parameters to update. + // NOTE: All parameters must be supplied. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{6} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{7} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +// MsgCreateFunToken: Arguments to create a "FunToken" mapping. Either the ERC20 +// contract address can be given to create the mapping to a Bank Coin, or the +// denomination for a Bank Coin can be given to create the mapping to an ERC20. +type MsgCreateFunToken struct { + // Hexadecimal address of the ERC20 token to which the `FunToken` maps + FromErc20 *github_com_NibiruChain_nibiru_v2_eth.EIP55Addr `protobuf:"bytes,1,opt,name=from_erc20,json=fromErc20,proto3,customtype=github.com/NibiruChain/nibiru/v2/eth.EIP55Addr" json:"from_erc20,omitempty"` + // Coin denomination in the Bank Module. + FromBankDenom string `protobuf:"bytes,2,opt,name=from_bank_denom,json=fromBankDenom,proto3" json:"from_bank_denom,omitempty"` + // Sender: Address for the signer of the transaction. + Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` +} + +func (m *MsgCreateFunToken) Reset() { *m = MsgCreateFunToken{} } +func (m *MsgCreateFunToken) String() string { return proto.CompactTextString(m) } +func (*MsgCreateFunToken) ProtoMessage() {} +func (*MsgCreateFunToken) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{8} +} +func (m *MsgCreateFunToken) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateFunToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateFunToken.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateFunToken) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateFunToken.Merge(m, src) +} +func (m *MsgCreateFunToken) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateFunToken) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateFunToken.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateFunToken proto.InternalMessageInfo + +func (m *MsgCreateFunToken) GetFromBankDenom() string { + if m != nil { + return m.FromBankDenom + } + return "" +} + +func (m *MsgCreateFunToken) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +type MsgCreateFunTokenResponse struct { + // Fungible token mapping corresponding to ERC20 tokens. + FuntokenMapping FunToken `protobuf:"bytes,1,opt,name=funtoken_mapping,json=funtokenMapping,proto3" json:"funtoken_mapping"` +} + +func (m *MsgCreateFunTokenResponse) Reset() { *m = MsgCreateFunTokenResponse{} } +func (m *MsgCreateFunTokenResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCreateFunTokenResponse) ProtoMessage() {} +func (*MsgCreateFunTokenResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{9} +} +func (m *MsgCreateFunTokenResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCreateFunTokenResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCreateFunTokenResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCreateFunTokenResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCreateFunTokenResponse.Merge(m, src) +} +func (m *MsgCreateFunTokenResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCreateFunTokenResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCreateFunTokenResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCreateFunTokenResponse proto.InternalMessageInfo + +func (m *MsgCreateFunTokenResponse) GetFuntokenMapping() FunToken { + if m != nil { + return m.FuntokenMapping + } + return FunToken{} +} + +// MsgConvertCoinToEvm: Arguments to send a Bank Coin to ERC-20 representation +type MsgConvertCoinToEvm struct { + // Hexadecimal address of the ERC20 token to which the `FunToken` maps + ToEthAddr github_com_NibiruChain_nibiru_v2_eth.EIP55Addr `protobuf:"bytes,1,opt,name=to_eth_addr,json=toEthAddr,proto3,customtype=github.com/NibiruChain/nibiru/v2/eth.EIP55Addr" json:"to_eth_addr"` + // Sender: Address for the signer of the transaction. + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` + // Bank Coin to get converted to ERC20 + BankCoin types1.Coin `protobuf:"bytes,3,opt,name=bank_coin,json=bankCoin,proto3" json:"bank_coin" yaml:"bank_coin"` +} + +func (m *MsgConvertCoinToEvm) Reset() { *m = MsgConvertCoinToEvm{} } +func (m *MsgConvertCoinToEvm) String() string { return proto.CompactTextString(m) } +func (*MsgConvertCoinToEvm) ProtoMessage() {} +func (*MsgConvertCoinToEvm) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{10} +} +func (m *MsgConvertCoinToEvm) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConvertCoinToEvm) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConvertCoinToEvm.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgConvertCoinToEvm) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConvertCoinToEvm.Merge(m, src) +} +func (m *MsgConvertCoinToEvm) XXX_Size() int { + return m.Size() +} +func (m *MsgConvertCoinToEvm) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConvertCoinToEvm.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConvertCoinToEvm proto.InternalMessageInfo + +func (m *MsgConvertCoinToEvm) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgConvertCoinToEvm) GetBankCoin() types1.Coin { + if m != nil { + return m.BankCoin + } + return types1.Coin{} +} + +type MsgConvertCoinToEvmResponse struct { +} + +func (m *MsgConvertCoinToEvmResponse) Reset() { *m = MsgConvertCoinToEvmResponse{} } +func (m *MsgConvertCoinToEvmResponse) String() string { return proto.CompactTextString(m) } +func (*MsgConvertCoinToEvmResponse) ProtoMessage() {} +func (*MsgConvertCoinToEvmResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{11} +} +func (m *MsgConvertCoinToEvmResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConvertCoinToEvmResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConvertCoinToEvmResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgConvertCoinToEvmResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConvertCoinToEvmResponse.Merge(m, src) +} +func (m *MsgConvertCoinToEvmResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgConvertCoinToEvmResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConvertCoinToEvmResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConvertCoinToEvmResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgEthereumTx)(nil), "eth.evm.v1.MsgEthereumTx") + proto.RegisterType((*LegacyTx)(nil), "eth.evm.v1.LegacyTx") + proto.RegisterType((*AccessListTx)(nil), "eth.evm.v1.AccessListTx") + proto.RegisterType((*DynamicFeeTx)(nil), "eth.evm.v1.DynamicFeeTx") + proto.RegisterType((*ExtensionOptionsEthereumTx)(nil), "eth.evm.v1.ExtensionOptionsEthereumTx") + proto.RegisterType((*MsgEthereumTxResponse)(nil), "eth.evm.v1.MsgEthereumTxResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "eth.evm.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "eth.evm.v1.MsgUpdateParamsResponse") + proto.RegisterType((*MsgCreateFunToken)(nil), "eth.evm.v1.MsgCreateFunToken") + proto.RegisterType((*MsgCreateFunTokenResponse)(nil), "eth.evm.v1.MsgCreateFunTokenResponse") + proto.RegisterType((*MsgConvertCoinToEvm)(nil), "eth.evm.v1.MsgConvertCoinToEvm") + proto.RegisterType((*MsgConvertCoinToEvmResponse)(nil), "eth.evm.v1.MsgConvertCoinToEvmResponse") +} + +func init() { proto.RegisterFile("eth/evm/v1/tx.proto", fileDescriptor_82a0bfe4f0bab953) } + +var fileDescriptor_82a0bfe4f0bab953 = []byte{ + // 1247 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xcf, 0xda, 0x8e, 0xff, 0x3c, 0xbb, 0x49, 0xd8, 0xa6, 0xd4, 0x76, 0x5b, 0x6f, 0xd8, 0x8a, + 0x12, 0x90, 0xb2, 0xdb, 0x18, 0xb5, 0x52, 0x73, 0x22, 0x4e, 0x5c, 0x54, 0x94, 0x40, 0xb4, 0x38, + 0x3d, 0x20, 0x24, 0x6b, 0xbc, 0x3b, 0x59, 0xaf, 0x92, 0x9d, 0x59, 0xed, 0x8c, 0x57, 0x0e, 0xc7, + 0x9e, 0x90, 0x38, 0x00, 0xe2, 0x0b, 0x70, 0xe0, 0xd4, 0x13, 0x87, 0x1e, 0xf8, 0x08, 0x15, 0xa7, + 0x0a, 0x0e, 0xa0, 0x1e, 0x0c, 0x4a, 0x91, 0x90, 0x7a, 0xec, 0x81, 0x1b, 0x12, 0x9a, 0xd9, 0xb5, + 0x63, 0x27, 0x38, 0x85, 0x08, 0x71, 0x9b, 0x37, 0xef, 0xcf, 0xbc, 0xf7, 0xfb, 0xbd, 0x79, 0x33, + 0x70, 0x11, 0xf3, 0xae, 0x89, 0x23, 0xdf, 0x8c, 0x56, 0x4d, 0xde, 0x37, 0x82, 0x90, 0x72, 0xaa, + 0x02, 0xe6, 0x5d, 0x03, 0x47, 0xbe, 0x11, 0xad, 0x56, 0x2f, 0xdb, 0x94, 0xf9, 0x94, 0x99, 0x3e, + 0x73, 0x85, 0x8d, 0xcf, 0xdc, 0xd8, 0xa8, 0x5a, 0x4b, 0x14, 0x1d, 0xc4, 0xb0, 0x19, 0xad, 0x76, + 0x30, 0x47, 0xab, 0xa6, 0x4d, 0x3d, 0x92, 0xe8, 0x2b, 0xb1, 0xbe, 0x2d, 0x25, 0x33, 0x16, 0x12, + 0xd5, 0xe2, 0xd8, 0xa1, 0xe2, 0x98, 0x64, 0xd7, 0xa5, 0x2e, 0x8d, 0xad, 0xc5, 0x2a, 0xd9, 0xbd, + 0xea, 0x52, 0xea, 0x1e, 0x60, 0x13, 0x05, 0x9e, 0x89, 0x08, 0xa1, 0x1c, 0x71, 0x8f, 0x92, 0x61, + 0xa4, 0x4a, 0xa2, 0x95, 0x52, 0xa7, 0xb7, 0x67, 0x22, 0x72, 0x18, 0xab, 0xf4, 0xcf, 0x15, 0xb8, + 0xb0, 0xcd, 0xdc, 0x26, 0xef, 0xe2, 0x10, 0xf7, 0xfc, 0x56, 0x5f, 0x5d, 0x86, 0x8c, 0x83, 0x38, + 0x2a, 0x2b, 0x4b, 0xca, 0x72, 0xb1, 0xbe, 0x68, 0xc4, 0xbe, 0xc6, 0xd0, 0xd7, 0x58, 0x27, 0x87, + 0x96, 0xb4, 0x50, 0x2b, 0x90, 0x61, 0xde, 0x27, 0xb8, 0x9c, 0x5a, 0x52, 0x96, 0x95, 0xc6, 0xec, + 0xf3, 0x81, 0xa6, 0xac, 0x58, 0x72, 0x4b, 0xd5, 0x20, 0xd3, 0x45, 0xac, 0x5b, 0x4e, 0x2f, 0x29, + 0xcb, 0x85, 0x46, 0xf1, 0xc5, 0x40, 0xcb, 0x85, 0x07, 0xc1, 0x9a, 0xbe, 0xa2, 0x5b, 0x52, 0xa1, + 0xaa, 0x90, 0xd9, 0x0b, 0xa9, 0x5f, 0xce, 0x08, 0x03, 0x4b, 0xae, 0xd7, 0x32, 0x9f, 0x7e, 0xad, + 0xcd, 0xe8, 0x5f, 0xa6, 0x20, 0xbf, 0x85, 0x5d, 0x64, 0x1f, 0xb6, 0xfa, 0xea, 0x22, 0xcc, 0x12, + 0x4a, 0x6c, 0x2c, 0xb3, 0xc9, 0x58, 0xb1, 0xa0, 0xde, 0x86, 0x82, 0x8b, 0x04, 0x66, 0x9e, 0x1d, + 0x9f, 0x5e, 0x68, 0x54, 0x9e, 0x0e, 0xb4, 0x4b, 0x31, 0x7c, 0xcc, 0xd9, 0x37, 0x3c, 0x6a, 0xfa, + 0x88, 0x77, 0x8d, 0x7b, 0x84, 0x5b, 0x79, 0x17, 0xb1, 0x1d, 0x61, 0xaa, 0xd6, 0x20, 0xed, 0x22, + 0x26, 0x93, 0xca, 0x34, 0x4a, 0x47, 0x03, 0x2d, 0xff, 0x2e, 0x62, 0x5b, 0x9e, 0xef, 0x71, 0x4b, + 0x28, 0xd4, 0x39, 0x48, 0x71, 0x9a, 0xa4, 0x94, 0xe2, 0x54, 0xbd, 0x03, 0xb3, 0x11, 0x3a, 0xe8, + 0xe1, 0xf2, 0xac, 0x3c, 0xe3, 0xfa, 0xd4, 0x33, 0x8e, 0x06, 0x5a, 0x76, 0xdd, 0xa7, 0x3d, 0xc2, + 0xad, 0xd8, 0x43, 0xd4, 0x27, 0x51, 0xcc, 0x2e, 0x29, 0xcb, 0xa5, 0x04, 0xaf, 0x12, 0x28, 0x51, + 0x39, 0x27, 0x37, 0x94, 0x48, 0x48, 0x61, 0x39, 0x1f, 0x4b, 0xa1, 0x90, 0x58, 0xb9, 0x10, 0x4b, + 0x6c, 0x6d, 0x4e, 0x20, 0xf1, 0xfd, 0xa3, 0x95, 0x6c, 0xab, 0xbf, 0x89, 0x38, 0xd2, 0xbf, 0x4b, + 0x43, 0x69, 0xdd, 0xb6, 0x31, 0x63, 0x5b, 0x1e, 0xe3, 0xad, 0xbe, 0xfa, 0x1e, 0xe4, 0xed, 0x2e, + 0xf2, 0x48, 0xdb, 0x73, 0x24, 0x34, 0x85, 0x86, 0x79, 0x56, 0x72, 0xb9, 0x0d, 0x61, 0x7c, 0x6f, + 0xf3, 0xf9, 0x40, 0xcb, 0xd9, 0xf1, 0xd2, 0x4a, 0x16, 0xce, 0x31, 0xc6, 0xa9, 0xa9, 0x18, 0xa7, + 0xff, 0x35, 0xc6, 0x99, 0xb3, 0x31, 0x9e, 0x3d, 0x8d, 0x71, 0xf6, 0xdc, 0x18, 0xe7, 0xc6, 0x30, + 0xde, 0x85, 0x3c, 0x92, 0x40, 0x61, 0x56, 0xce, 0x2f, 0xa5, 0x97, 0x8b, 0xf5, 0xcb, 0xc6, 0xf1, + 0x3d, 0x35, 0x62, 0x10, 0x5b, 0xbd, 0xe0, 0x00, 0x37, 0x96, 0x1e, 0x0f, 0xb4, 0x99, 0xe7, 0x03, + 0x0d, 0xd0, 0x08, 0xd9, 0x87, 0xbf, 0x68, 0x70, 0x8c, 0xb3, 0x35, 0x0a, 0x15, 0x53, 0x57, 0x98, + 0xa0, 0x0e, 0x26, 0xa8, 0x2b, 0x4e, 0xa3, 0xee, 0x8f, 0x34, 0x94, 0x36, 0x0f, 0x09, 0xf2, 0x3d, + 0xfb, 0x2e, 0xc6, 0xff, 0x0b, 0x75, 0x77, 0xa0, 0x28, 0xa8, 0xe3, 0x5e, 0xd0, 0xb6, 0x51, 0xf0, + 0x72, 0xf2, 0x04, 0xd1, 0x2d, 0x2f, 0xd8, 0x40, 0xc1, 0xd0, 0x75, 0x0f, 0x63, 0xe9, 0x9a, 0xf9, + 0x27, 0xae, 0x77, 0x31, 0x16, 0xae, 0x09, 0xf1, 0xb3, 0x67, 0x13, 0x9f, 0x3d, 0x4d, 0x7c, 0xee, + 0xdc, 0xc4, 0xe7, 0xa7, 0x10, 0x5f, 0xf8, 0x8f, 0x89, 0x87, 0x09, 0xe2, 0x8b, 0x13, 0xc4, 0x97, + 0xa6, 0x11, 0xaf, 0x43, 0xb5, 0xd9, 0xe7, 0x98, 0x30, 0x8f, 0x92, 0x0f, 0x02, 0x39, 0x8e, 0x8f, + 0xa7, 0x6c, 0x32, 0xeb, 0xbe, 0x51, 0xe0, 0xd2, 0xc4, 0xf4, 0xb5, 0x30, 0x0b, 0x28, 0x61, 0xb2, + 0x44, 0x39, 0x40, 0x95, 0x78, 0x3e, 0xca, 0x99, 0xf9, 0x26, 0x64, 0x0e, 0xa8, 0xcb, 0xca, 0x29, + 0x59, 0xde, 0xfc, 0x78, 0x79, 0x5b, 0xd4, 0x6d, 0x64, 0x44, 0x59, 0x96, 0x34, 0x51, 0x17, 0x20, + 0x1d, 0x62, 0x2e, 0xa9, 0x2f, 0x59, 0x62, 0xa9, 0x56, 0x20, 0x1f, 0xf9, 0x6d, 0x1c, 0x86, 0x34, + 0x4c, 0x26, 0x5c, 0x2e, 0xf2, 0x9b, 0x42, 0x14, 0x2a, 0x41, 0x7a, 0x8f, 0x61, 0x27, 0xa6, 0xcf, + 0xca, 0xb9, 0x88, 0xed, 0x32, 0xec, 0x24, 0x69, 0x7e, 0xa6, 0xc0, 0xfc, 0x36, 0x73, 0x77, 0x03, + 0x07, 0x71, 0xbc, 0x83, 0x42, 0xe4, 0x33, 0x31, 0x1f, 0x50, 0x8f, 0x77, 0x69, 0xe8, 0xf1, 0xc3, + 0xa4, 0x8f, 0xcb, 0x3f, 0x3c, 0x5a, 0x59, 0x4c, 0x9e, 0xb0, 0x75, 0xc7, 0x09, 0x31, 0x63, 0x1f, + 0xf2, 0xd0, 0x23, 0xae, 0x75, 0x6c, 0xaa, 0xde, 0x84, 0x6c, 0x20, 0x23, 0xc8, 0x9e, 0x2d, 0xd6, + 0xd5, 0xf1, 0x32, 0xe2, 0xd8, 0x49, 0x25, 0x89, 0xdd, 0xda, 0xdc, 0x83, 0xdf, 0xbf, 0x7d, 0xeb, + 0x38, 0x82, 0x5e, 0x81, 0xcb, 0x27, 0x92, 0x19, 0xa2, 0xa6, 0x3f, 0x54, 0xe0, 0x95, 0x6d, 0xe6, + 0x6e, 0x84, 0x18, 0x71, 0x7c, 0xb7, 0x47, 0x5a, 0x74, 0x1f, 0x13, 0x75, 0x17, 0x40, 0xbc, 0x2f, + 0x6d, 0x1c, 0xda, 0xf5, 0x9b, 0x49, 0xae, 0xb7, 0x1f, 0x0f, 0x34, 0xe5, 0xe9, 0x40, 0x33, 0x5c, + 0x8f, 0x77, 0x7b, 0x1d, 0xc3, 0xa6, 0xbe, 0xf9, 0xbe, 0xd7, 0xf1, 0xc2, 0x9e, 0xbc, 0x6f, 0x26, + 0x91, 0x6b, 0x33, 0xaa, 0x9b, 0x22, 0xbd, 0xe6, 0xbd, 0x9d, 0x5b, 0xb7, 0x44, 0x49, 0x56, 0x41, + 0x44, 0x6a, 0x8a, 0x40, 0xea, 0x0d, 0x98, 0x97, 0x61, 0x3b, 0x88, 0xec, 0xb7, 0x1d, 0x4c, 0xa8, + 0x1f, 0xbf, 0x45, 0xd6, 0x05, 0xb1, 0xdd, 0x40, 0x64, 0x7f, 0x53, 0x6c, 0xaa, 0xaf, 0x42, 0x96, + 0x61, 0xe2, 0xe0, 0x30, 0xbe, 0x89, 0x56, 0x22, 0xe9, 0x1d, 0xa8, 0x9c, 0xca, 0x75, 0xc4, 0x7f, + 0x13, 0x16, 0xf6, 0x7a, 0x84, 0x8b, 0xbd, 0xb6, 0x8f, 0x82, 0xc0, 0x23, 0xee, 0xe8, 0x45, 0x1e, + 0x03, 0x6c, 0xe8, 0x97, 0x40, 0x36, 0x3f, 0xf4, 0xd9, 0x8e, 0x5d, 0xf4, 0x9f, 0x14, 0xb8, 0x28, + 0x0e, 0xa1, 0x24, 0xc2, 0x21, 0xdf, 0xa0, 0x1e, 0x69, 0xd1, 0x66, 0xe4, 0xab, 0xf7, 0xa1, 0xc8, + 0x69, 0x1b, 0xf3, 0x6e, 0x1b, 0x39, 0x4e, 0x38, 0x86, 0xc9, 0xcc, 0x79, 0x30, 0xe1, 0xb4, 0xc9, + 0xbb, 0x62, 0x39, 0x56, 0x6b, 0x6a, 0xbc, 0x56, 0x75, 0x07, 0x0a, 0x12, 0x26, 0xf1, 0xf3, 0x91, + 0x30, 0x14, 0xeb, 0x15, 0x23, 0x69, 0x15, 0xf1, 0x35, 0x32, 0x92, 0xaf, 0x91, 0x21, 0x52, 0x6c, + 0x94, 0x45, 0x22, 0x2f, 0x06, 0xda, 0xc2, 0x21, 0xf2, 0x0f, 0xd6, 0xf4, 0x91, 0xa7, 0x6e, 0xe5, + 0xc5, 0x5a, 0xd8, 0xe8, 0xd7, 0xe0, 0xca, 0xdf, 0x14, 0x36, 0xc4, 0xaf, 0xfe, 0x67, 0x0a, 0xd2, + 0xdb, 0xcc, 0x55, 0x09, 0xc0, 0xd8, 0xdf, 0xa6, 0x32, 0x8e, 0xdd, 0xc4, 0xc5, 0xab, 0xbe, 0x36, + 0x55, 0x35, 0xea, 0x2e, 0xfd, 0xc1, 0x8f, 0xbf, 0x7d, 0x95, 0xba, 0xaa, 0x57, 0x87, 0x48, 0x0c, + 0x3f, 0x67, 0x89, 0x69, 0x9b, 0xf7, 0xd5, 0x1d, 0x28, 0x4d, 0x5c, 0x93, 0x2b, 0x27, 0xc2, 0x8e, + 0x2b, 0xab, 0xd7, 0xcf, 0x50, 0x8e, 0x3a, 0xe1, 0x3e, 0xcc, 0x9d, 0xe8, 0xe7, 0x6b, 0x27, 0xdc, + 0x26, 0xd5, 0xd5, 0xd7, 0xcf, 0x54, 0x8f, 0xe2, 0x7e, 0x0c, 0x0b, 0xa7, 0xda, 0x42, 0x3b, 0xe9, + 0x7a, 0xc2, 0xa0, 0xfa, 0xc6, 0x4b, 0x0c, 0x86, 0xd1, 0x1b, 0xef, 0x3c, 0x3e, 0xaa, 0x29, 0x4f, + 0x8e, 0x6a, 0xca, 0xaf, 0x47, 0x35, 0xe5, 0x8b, 0x67, 0xb5, 0x99, 0x27, 0xcf, 0x6a, 0x33, 0x3f, + 0x3f, 0xab, 0xcd, 0x7c, 0x74, 0xe3, 0xa5, 0xdd, 0xd5, 0x17, 0xc0, 0x76, 0xb2, 0xf2, 0xc7, 0xf9, + 0xf6, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xdd, 0x8f, 0x11, 0x7c, 0x0b, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // EthereumTx defines a method submitting Ethereum transactions. + EthereumTx(ctx context.Context, in *MsgEthereumTx, opts ...grpc.CallOption) (*MsgEthereumTxResponse, error) + // UpdateParams defined a governance operation for updating the x/evm module + // parameters. The authority is hard-coded to the x/gov module account + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) + // CreateFunToken: Create a "FunToken" mapping. Either the ERC20 contract + // address can be given to create the mapping to a Bank Coin, or the + // denomination for a Bank Coin can be given to create the mapping to an + // ERC20. + CreateFunToken(ctx context.Context, in *MsgCreateFunToken, opts ...grpc.CallOption) (*MsgCreateFunTokenResponse, error) + // ConvertCoinToEvm: Sends a coin with a valid "FunToken" mapping to the + // given recipient address ("to_eth_addr") in the corresponding ERC20 + // representation. + ConvertCoinToEvm(ctx context.Context, in *MsgConvertCoinToEvm, opts ...grpc.CallOption) (*MsgConvertCoinToEvmResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) EthereumTx(ctx context.Context, in *MsgEthereumTx, opts ...grpc.CallOption) (*MsgEthereumTxResponse, error) { + out := new(MsgEthereumTxResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Msg/EthereumTx", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CreateFunToken(ctx context.Context, in *MsgCreateFunToken, opts ...grpc.CallOption) (*MsgCreateFunTokenResponse, error) { + out := new(MsgCreateFunTokenResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Msg/CreateFunToken", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) ConvertCoinToEvm(ctx context.Context, in *MsgConvertCoinToEvm, opts ...grpc.CallOption) (*MsgConvertCoinToEvmResponse, error) { + out := new(MsgConvertCoinToEvmResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Msg/ConvertCoinToEvm", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // EthereumTx defines a method submitting Ethereum transactions. + EthereumTx(context.Context, *MsgEthereumTx) (*MsgEthereumTxResponse, error) + // UpdateParams defined a governance operation for updating the x/evm module + // parameters. The authority is hard-coded to the x/gov module account + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) + // CreateFunToken: Create a "FunToken" mapping. Either the ERC20 contract + // address can be given to create the mapping to a Bank Coin, or the + // denomination for a Bank Coin can be given to create the mapping to an + // ERC20. + CreateFunToken(context.Context, *MsgCreateFunToken) (*MsgCreateFunTokenResponse, error) + // ConvertCoinToEvm: Sends a coin with a valid "FunToken" mapping to the + // given recipient address ("to_eth_addr") in the corresponding ERC20 + // representation. + ConvertCoinToEvm(context.Context, *MsgConvertCoinToEvm) (*MsgConvertCoinToEvmResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) EthereumTx(ctx context.Context, req *MsgEthereumTx) (*MsgEthereumTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EthereumTx not implemented") +} +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} +func (*UnimplementedMsgServer) CreateFunToken(ctx context.Context, req *MsgCreateFunToken) (*MsgCreateFunTokenResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateFunToken not implemented") +} +func (*UnimplementedMsgServer) ConvertCoinToEvm(ctx context.Context, req *MsgConvertCoinToEvm) (*MsgConvertCoinToEvmResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConvertCoinToEvm not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_EthereumTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgEthereumTx) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).EthereumTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Msg/EthereumTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).EthereumTx(ctx, req.(*MsgEthereumTx)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CreateFunToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCreateFunToken) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CreateFunToken(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Msg/CreateFunToken", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CreateFunToken(ctx, req.(*MsgCreateFunToken)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_ConvertCoinToEvm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConvertCoinToEvm) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConvertCoinToEvm(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Msg/ConvertCoinToEvm", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConvertCoinToEvm(ctx, req.(*MsgConvertCoinToEvm)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "eth.evm.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "EthereumTx", + Handler: _Msg_EthereumTx_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + { + MethodName: "CreateFunToken", + Handler: _Msg_CreateFunToken_Handler, + }, + { + MethodName: "ConvertCoinToEvm", + Handler: _Msg_ConvertCoinToEvm_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "eth/evm/v1/tx.proto", +} + +func (m *MsgEthereumTx) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgEthereumTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgEthereumTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.From) > 0 { + i -= len(m.From) + copy(dAtA[i:], m.From) + i = encodeVarintTx(dAtA, i, uint64(len(m.From))) + i-- + dAtA[i] = 0x22 + } + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintTx(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0x1a + } + if m.Size_ != 0 { + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Size_)))) + i-- + dAtA[i] = 0x11 + } + if m.Data != nil { + { + size, err := m.Data.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LegacyTx) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LegacyTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LegacyTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.S) > 0 { + i -= len(m.S) + copy(dAtA[i:], m.S) + i = encodeVarintTx(dAtA, i, uint64(len(m.S))) + i-- + dAtA[i] = 0x4a + } + if len(m.R) > 0 { + i -= len(m.R) + copy(dAtA[i:], m.R) + i = encodeVarintTx(dAtA, i, uint64(len(m.R))) + i-- + dAtA[i] = 0x42 + } + if len(m.V) > 0 { + i -= len(m.V) + copy(dAtA[i:], m.V) + i = encodeVarintTx(dAtA, i, uint64(len(m.V))) + i-- + dAtA[i] = 0x3a + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintTx(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x32 + } + if m.Amount != nil { + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.To) > 0 { + i -= len(m.To) + copy(dAtA[i:], m.To) + i = encodeVarintTx(dAtA, i, uint64(len(m.To))) + i-- + dAtA[i] = 0x22 + } + if m.GasLimit != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.GasLimit)) + i-- + dAtA[i] = 0x18 + } + if m.GasPrice != nil { + { + size := m.GasPrice.Size() + i -= size + if _, err := m.GasPrice.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Nonce != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *AccessListTx) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AccessListTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AccessListTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.S) > 0 { + i -= len(m.S) + copy(dAtA[i:], m.S) + i = encodeVarintTx(dAtA, i, uint64(len(m.S))) + i-- + dAtA[i] = 0x5a + } + if len(m.R) > 0 { + i -= len(m.R) + copy(dAtA[i:], m.R) + i = encodeVarintTx(dAtA, i, uint64(len(m.R))) + i-- + dAtA[i] = 0x52 + } + if len(m.V) > 0 { + i -= len(m.V) + copy(dAtA[i:], m.V) + i = encodeVarintTx(dAtA, i, uint64(len(m.V))) + i-- + dAtA[i] = 0x4a + } + if len(m.Accesses) > 0 { + for iNdEx := len(m.Accesses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Accesses[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintTx(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x3a + } + if m.Amount != nil { + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if len(m.To) > 0 { + i -= len(m.To) + copy(dAtA[i:], m.To) + i = encodeVarintTx(dAtA, i, uint64(len(m.To))) + i-- + dAtA[i] = 0x2a + } + if m.GasLimit != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.GasLimit)) + i-- + dAtA[i] = 0x20 + } + if m.GasPrice != nil { + { + size := m.GasPrice.Size() + i -= size + if _, err := m.GasPrice.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Nonce != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x10 + } + if m.ChainID != nil { + { + size := m.ChainID.Size() + i -= size + if _, err := m.ChainID.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DynamicFeeTx) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DynamicFeeTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DynamicFeeTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.S) > 0 { + i -= len(m.S) + copy(dAtA[i:], m.S) + i = encodeVarintTx(dAtA, i, uint64(len(m.S))) + i-- + dAtA[i] = 0x62 + } + if len(m.R) > 0 { + i -= len(m.R) + copy(dAtA[i:], m.R) + i = encodeVarintTx(dAtA, i, uint64(len(m.R))) + i-- + dAtA[i] = 0x5a + } + if len(m.V) > 0 { + i -= len(m.V) + copy(dAtA[i:], m.V) + i = encodeVarintTx(dAtA, i, uint64(len(m.V))) + i-- + dAtA[i] = 0x52 + } + if len(m.Accesses) > 0 { + for iNdEx := len(m.Accesses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Accesses[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintTx(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x42 + } + if m.Amount != nil { + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + if len(m.To) > 0 { + i -= len(m.To) + copy(dAtA[i:], m.To) + i = encodeVarintTx(dAtA, i, uint64(len(m.To))) + i-- + dAtA[i] = 0x32 + } + if m.GasLimit != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.GasLimit)) + i-- + dAtA[i] = 0x28 + } + if m.GasFeeCap != nil { + { + size := m.GasFeeCap.Size() + i -= size + if _, err := m.GasFeeCap.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.GasTipCap != nil { + { + size := m.GasTipCap.Size() + i -= size + if _, err := m.GasTipCap.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Nonce != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Nonce)) + i-- + dAtA[i] = 0x10 + } + if m.ChainID != nil { + { + size := m.ChainID.Size() + i -= size + if _, err := m.ChainID.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExtensionOptionsEthereumTx) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExtensionOptionsEthereumTx) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExtensionOptionsEthereumTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgEthereumTxResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgEthereumTxResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgEthereumTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.GasUsed != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.GasUsed)) + i-- + dAtA[i] = 0x28 + } + if len(m.VmError) > 0 { + i -= len(m.VmError) + copy(dAtA[i:], m.VmError) + i = encodeVarintTx(dAtA, i, uint64(len(m.VmError))) + i-- + dAtA[i] = 0x22 + } + if len(m.Ret) > 0 { + i -= len(m.Ret) + copy(dAtA[i:], m.Ret) + i = encodeVarintTx(dAtA, i, uint64(len(m.Ret))) + i-- + dAtA[i] = 0x1a + } + if len(m.Logs) > 0 { + for iNdEx := len(m.Logs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Logs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintTx(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCreateFunToken) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateFunToken) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateFunToken) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x1a + } + if len(m.FromBankDenom) > 0 { + i -= len(m.FromBankDenom) + copy(dAtA[i:], m.FromBankDenom) + i = encodeVarintTx(dAtA, i, uint64(len(m.FromBankDenom))) + i-- + dAtA[i] = 0x12 + } + if m.FromErc20 != nil { + { + size := m.FromErc20.Size() + i -= size + if _, err := m.FromErc20.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCreateFunTokenResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCreateFunTokenResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCreateFunTokenResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.FuntokenMapping.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgConvertCoinToEvm) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgConvertCoinToEvm) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConvertCoinToEvm) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.BankCoin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x12 + } + { + size := m.ToEthAddr.Size() + i -= size + if _, err := m.ToEthAddr.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *MsgConvertCoinToEvmResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgConvertCoinToEvmResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConvertCoinToEvmResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgEthereumTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Data != nil { + l = m.Data.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.Size_ != 0 { + n += 9 + } + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.From) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *LegacyTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Nonce != 0 { + n += 1 + sovTx(uint64(m.Nonce)) + } + if m.GasPrice != nil { + l = m.GasPrice.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.GasLimit != 0 { + n += 1 + sovTx(uint64(m.GasLimit)) + } + l = len(m.To) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Amount != nil { + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.V) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.R) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.S) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *AccessListTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ChainID != nil { + l = m.ChainID.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.Nonce != 0 { + n += 1 + sovTx(uint64(m.Nonce)) + } + if m.GasPrice != nil { + l = m.GasPrice.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.GasLimit != 0 { + n += 1 + sovTx(uint64(m.GasLimit)) + } + l = len(m.To) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Amount != nil { + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Accesses) > 0 { + for _, e := range m.Accesses { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.V) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.R) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.S) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *DynamicFeeTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ChainID != nil { + l = m.ChainID.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.Nonce != 0 { + n += 1 + sovTx(uint64(m.Nonce)) + } + if m.GasTipCap != nil { + l = m.GasTipCap.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.GasFeeCap != nil { + l = m.GasFeeCap.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.GasLimit != 0 { + n += 1 + sovTx(uint64(m.GasLimit)) + } + l = len(m.To) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Amount != nil { + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Accesses) > 0 { + for _, e := range m.Accesses { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.V) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.R) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.S) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *ExtensionOptionsEthereumTx) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgEthereumTxResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Logs) > 0 { + for _, e := range m.Logs { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + l = len(m.Ret) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.VmError) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.GasUsed != 0 { + n += 1 + sovTx(uint64(m.GasUsed)) + } + return n +} + +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCreateFunToken) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FromErc20 != nil { + l = m.FromErc20.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.FromBankDenom) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCreateFunTokenResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.FuntokenMapping.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgConvertCoinToEvm) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ToEthAddr.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.BankCoin.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgConvertCoinToEvmResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgEthereumTx) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgEthereumTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgEthereumTx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Data == nil { + m.Data = &types.Any{} + } + if err := m.Data.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) + } + var v uint64 + if (iNdEx + 8) > l { + return io.ErrUnexpectedEOF + } + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) + iNdEx += 8 + m.Size_ = float64(math.Float64frombits(v)) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field From", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.From = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LegacyTx) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LegacyTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LegacyTx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Nonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasPrice", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.GasPrice = &v + if err := m.GasPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasLimit", wireType) + } + m.GasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field To", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.To = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.Amount = &v + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field V", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.V = append(m.V[:0], dAtA[iNdEx:postIndex]...) + if m.V == nil { + m.V = []byte{} + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field R", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.R = append(m.R[:0], dAtA[iNdEx:postIndex]...) + if m.R == nil { + m.R = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field S", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.S = append(m.S[:0], dAtA[iNdEx:postIndex]...) + if m.S == nil { + m.S = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AccessListTx) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AccessListTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AccessListTx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.ChainID = &v + if err := m.ChainID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Nonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasPrice", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.GasPrice = &v + if err := m.GasPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasLimit", wireType) + } + m.GasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field To", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.To = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.Amount = &v + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Accesses", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Accesses = append(m.Accesses, AccessTuple{}) + if err := m.Accesses[len(m.Accesses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field V", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.V = append(m.V[:0], dAtA[iNdEx:postIndex]...) + if m.V == nil { + m.V = []byte{} + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field R", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.R = append(m.R[:0], dAtA[iNdEx:postIndex]...) + if m.R == nil { + m.R = []byte{} + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field S", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.S = append(m.S[:0], dAtA[iNdEx:postIndex]...) + if m.S == nil { + m.S = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DynamicFeeTx) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DynamicFeeTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DynamicFeeTx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.ChainID = &v + if err := m.ChainID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType) + } + m.Nonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Nonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasTipCap", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.GasTipCap = &v + if err := m.GasTipCap.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasFeeCap", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.GasFeeCap = &v + if err := m.GasFeeCap.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasLimit", wireType) + } + m.GasLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field To", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.To = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v cosmossdk_io_math.Int + m.Amount = &v + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Accesses", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Accesses = append(m.Accesses, AccessTuple{}) + if err := m.Accesses[len(m.Accesses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field V", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.V = append(m.V[:0], dAtA[iNdEx:postIndex]...) + if m.V == nil { + m.V = []byte{} + } + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field R", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.R = append(m.R[:0], dAtA[iNdEx:postIndex]...) + if m.R == nil { + m.R = []byte{} + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field S", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.S = append(m.S[:0], dAtA[iNdEx:postIndex]...) + if m.S == nil { + m.S = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExtensionOptionsEthereumTx) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExtensionOptionsEthereumTx: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExtensionOptionsEthereumTx: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgEthereumTxResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgEthereumTxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgEthereumTxResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Logs = append(m.Logs, Log{}) + if err := m.Logs[len(m.Logs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ret", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Ret = append(m.Ret[:0], dAtA[iNdEx:postIndex]...) + if m.Ret == nil { + m.Ret = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VmError", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VmError = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType) + } + m.GasUsed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GasUsed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateFunToken) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateFunToken: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateFunToken: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromErc20", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_NibiruChain_nibiru_v2_eth.EIP55Addr + m.FromErc20 = &v + if err := m.FromErc20.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FromBankDenom", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FromBankDenom = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCreateFunTokenResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCreateFunTokenResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCreateFunTokenResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FuntokenMapping", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.FuntokenMapping.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgConvertCoinToEvm) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgConvertCoinToEvm: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConvertCoinToEvm: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToEthAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ToEthAddr.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BankCoin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BankCoin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgConvertCoinToEvmResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgConvertCoinToEvmResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConvertCoinToEvmResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/evm/tx.pb.gw.go b/x/evm/tx.pb.gw.go new file mode 100644 index 000000000..2a54a3b55 --- /dev/null +++ b/x/evm/tx.pb.gw.go @@ -0,0 +1,171 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: eth/evm/v1/tx.proto + +/* +Package evm is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package evm + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +var ( + filter_Msg_EthereumTx_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Msg_EthereumTx_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq MsgEthereumTx + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_EthereumTx_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.EthereumTx(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Msg_EthereumTx_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq MsgEthereumTx + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_EthereumTx_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.EthereumTx(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterMsgHandlerServer registers the http handlers for service Msg to "mux". +// UnaryRPC :call MsgServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMsgHandlerFromEndpoint instead. +func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MsgServer) error { + + mux.Handle("POST", pattern_Msg_EthereumTx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Msg_EthereumTx_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Msg_EthereumTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterMsgHandlerFromEndpoint is same as RegisterMsgHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterMsgHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterMsgHandler(ctx, mux, conn) +} + +// RegisterMsgHandler registers the http handlers for service Msg to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterMsgHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterMsgHandlerClient(ctx, mux, NewMsgClient(conn)) +} + +// RegisterMsgHandlerClient registers the http handlers for service Msg +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MsgClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MsgClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "MsgClient" to call the correct interceptors. +func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MsgClient) error { + + mux.Handle("POST", pattern_Msg_EthereumTx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Msg_EthereumTx_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Msg_EthereumTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Msg_EthereumTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"nibiru", "evm", "v1", "ethereum_tx"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Msg_EthereumTx_0 = runtime.ForwardResponseMessage +) diff --git a/x/evm/tx_data.go b/x/evm/tx_data.go new file mode 100644 index 000000000..c8bf3720c --- /dev/null +++ b/x/evm/tx_data.go @@ -0,0 +1,242 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "math/big" + + errorsmod "cosmossdk.io/errors" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +var ( + _ TxData = &LegacyTx{} + _ TxData = &AccessListTx{} + _ TxData = &DynamicFeeTx{} +) + +// TxData is the underlying data of a transaction. Its counterpart with private +// fields, "gethcore.TxData" is implemented by DynamicFeeTx, LegacyTx and +// AccessListTx from the same package. Each trnsaction type is implemented here +// for protobuf marshaling. +// +// According to https://github.com/ethereum/go-ethereum/issues/23154: +// TxData exists for the sole purpose of making it easier to construct a +// "gethcore.Transaction" more conviently in Go code. The methods of TxData are +// an internal implementation detail and will never have a stable API. +// +// Because the fields are private in the go-ethereum code, it is impossible to +// provide custom implementations for these methods without creating a new TxData +// data structure. Thus, the current interface exists. +type TxData interface { + // TxType returns a byte (uint8) identifying the tx as a [LegacyTx] (0), + // [AccessListTx] (1), or [DynamicFeeTx] (2). + TxType() byte + Copy() TxData + GetChainID() *big.Int + GetAccessList() gethcore.AccessList + GetData() []byte + + GetNonce() uint64 + + // GetGas returns the gas limit in gas units. Note that this is not a "fee" + // in wei or micronibi or a price. + GetGas() uint64 + + // Gas price as wei spent per unit gas. + GetGasPrice() *big.Int + + // GetGasTipCapWei returns a cap on the gas tip in units of wei. + // + // Also called "maxPriorityFeePerGas" in Alchemy and Ethers. + // See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]. + // Base fees are determined by the network, not the end user that broadcasts + // the transaction. Adding a tip increases one's "priority" in the block. + // + // The terminology "fee per gas" essentially means "wei per unit gas". + // See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas] for more info. + // + // [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]: https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas. + GetGasTipCapWei() *big.Int + + // GetGasFeeCapWei returns a cap on the gas fees paid in units of wei, where: + // feesWithoutCap := effective gas price (wei per gas) * gas units + // fees -> min(feesWithoutCap, gasFeeCap) + // Also called "maxFeePerGas" in Alchemy and Ethers. + // + // maxFeePerGas := baseFeePerGas + maxPriorityFeePerGas + // + // The terminology "fee per gas" essentially means "wei per unit gas". + // See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas] for more info. + // + // [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]: https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas. + GetGasFeeCapWei() *big.Int + EffectiveGasFeeCapWei(baseFeeWei *big.Int) *big.Int + + // GetValueWei: amount of ether (wei units) sent in the transaction. + GetValueWei() *big.Int + + GetTo() *common.Address + GetToRaw() string + + GetRawSignatureValues() (v, r, s *big.Int) + SetSignatureValues(chainID, v, r, s *big.Int) + + AsEthereumData() gethcore.TxData + Validate() error + + // Fee in units of wei := gasPrice (wei per gas) * gasLimit (gas). + Fee() *big.Int + // Cost in units of wei := Fee * Value. Cost is the gas cost of the + // transaction in wei, accounting for value transferred. + Cost() *big.Int + + // EffectiveGasPriceWeiPerGas is effective gas price in units of wei per gas. + // "Effective" means depending on the base fee of the network. + EffectiveGasPriceWeiPerGas(baseFeeWei *big.Int) *big.Int + + // EffectiveFeeWei := effectiveGasPrice (wei per gas) * gasLimit (gas). + // "Effective" means depending on the base fee of the network. + EffectiveFeeWei(baseFeeWei *big.Int) *big.Int + + // EffectiveCostWei is the same as cost, except it uses effective fee in wei + // rathen than fee. Effective cost is the gas cost of the transaction in wei, + // accounting for value (wei) transferred and using effective gas price (wei + // per gas). + EffectiveCostWei(baseFeeWei *big.Int) *big.Int +} + +// NOTE: All non-protected transactions (i.e. non EIP155 signed) will fail if the +// AllowUnprotectedTxs parameter is disabled. +func NewTxDataFromTx(tx *gethcore.Transaction) (TxData, error) { + var txData TxData + var err error + switch tx.Type() { + case gethcore.DynamicFeeTxType: + txData, err = NewDynamicFeeTx(tx) + case gethcore.AccessListTxType: + txData, err = newAccessListTx(tx) + default: + txData, err = NewLegacyTx(tx) + } + if err != nil { + return nil, err + } + + return txData, nil +} + +// DeriveChainID derives the chain id from the given v parameter. +// +// CONTRACT: v value is either: +// +// - {0,1} + CHAIN_ID * 2 + 35, if EIP155 is used +// - {0,1} + 27, otherwise +// +// Ref: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md +func DeriveChainID(v *big.Int) *big.Int { + if v == nil || v.Sign() < 1 { + return nil + } + + if v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + + if v < 35 { + return nil + } + + // V MUST be of the form {0,1} + CHAIN_ID * 2 + 35 + return new(big.Int).SetUint64((v - 35) / 2) + } + v = new(big.Int).Sub(v, big.NewInt(35)) + return v.Div(v, big.NewInt(2)) +} + +func rawSignatureValues(vBz, rBz, sBz []byte) (v, r, s *big.Int) { + if len(vBz) > 0 { + v = new(big.Int).SetBytes(vBz) + } + if len(rBz) > 0 { + r = new(big.Int).SetBytes(rBz) + } + if len(sBz) > 0 { + s = new(big.Int).SetBytes(sBz) + } + return v, r, s +} + +// Returns the fee in wei corresponding to the given gas price and gas amount. +// Args: +// - weiPerGas: Wei per unit gas (gas price). +// - gas: gas units +func priceTimesGas(weiPerGas *big.Int, gas uint64) *big.Int { + gasLimit := new(big.Int).SetUint64(gas) + return new(big.Int).Mul(weiPerGas, gasLimit) +} + +func cost(fee, value *big.Int) *big.Int { + if value != nil { + return new(big.Int).Add(fee, value) + } + return fee +} + +func (tx *DynamicFeeTx) GetToRaw() string { return tx.To } +func (tx *LegacyTx) GetToRaw() string { return tx.To } +func (tx *AccessListTx) GetToRaw() string { return tx.To } + +func ValidateTxDataAmount(txData TxData) error { + amount := txData.GetValueWei() + // Amount can be 0 + if amount != nil && amount.Sign() == -1 { + return errorsmod.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount) + } + if !eth.IsValidInt256(amount) { + return errorsmod.Wrap(ErrInvalidAmount, "out of bound") + } + return nil +} + +func ValidateTxDataTo(txData TxData) error { + to := txData.GetToRaw() + if to != "" { + if err := eth.ValidateAddress(to); err != nil { + return errorsmod.Wrap(err, "invalid to address") + } + } + return nil +} + +func ValidateTxDataGasPrice(txData TxData) error { + gasPrice := txData.GetGasPrice() + if gasPrice == nil { + return errorsmod.Wrap(ErrInvalidGasPrice, "cannot be nil") + } + if !eth.IsValidInt256(gasPrice) { + return errorsmod.Wrap(ErrInvalidGasPrice, "out of bound") + } + + if gasPrice.Sign() == -1 { + return errorsmod.Wrapf(ErrInvalidGasPrice, "gas price cannot be negative %s", gasPrice) + } + return nil +} + +func ValidateTxDataChainID(txData TxData) error { + chainID := txData.GetChainID() + + if chainID == nil { + return errorsmod.Wrap( + sdkerrors.ErrInvalidChainID, + "chain ID must be derived from TxData txs", + ) + } + return nil +} diff --git a/x/evm/tx_data_access_list.go b/x/evm/tx_data_access_list.go new file mode 100644 index 000000000..acbcca26b --- /dev/null +++ b/x/evm/tx_data_access_list.go @@ -0,0 +1,309 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "math/big" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// AccessList is an EIP-2930 access list that represents the slice of +// the protobuf AccessTuples. +type AccessList []AccessTuple + +// NewAccessList creates a new protobuf-compatible AccessList from an ethereum +// core AccessList type +func NewAccessList(ethAccessList *gethcore.AccessList) AccessList { + if ethAccessList == nil { + return nil + } + + al := AccessList{} + for _, tuple := range *ethAccessList { + storageKeys := make([]string, len(tuple.StorageKeys)) + + for i := range tuple.StorageKeys { + storageKeys[i] = tuple.StorageKeys[i].String() + } + + al = append(al, AccessTuple{ + Address: tuple.Address.String(), + StorageKeys: storageKeys, + }) + } + + return al +} + +// ToEthAccessList is a utility function to convert the protobuf compatible +// AccessList to eth core AccessList from go-ethereum +func (al AccessList) ToEthAccessList() *gethcore.AccessList { + var ethAccessList gethcore.AccessList + + for _, tuple := range al { + storageKeys := make([]common.Hash, len(tuple.StorageKeys)) + + for i := range tuple.StorageKeys { + storageKeys[i] = common.HexToHash(tuple.StorageKeys[i]) + } + + ethAccessList = append(ethAccessList, gethcore.AccessTuple{ + Address: common.HexToAddress(tuple.Address), + StorageKeys: storageKeys, + }) + } + + return ðAccessList +} + +// AccessListTx + +func newAccessListTx(tx *gethcore.Transaction) (*AccessListTx, error) { + txData := &AccessListTx{ + Nonce: tx.Nonce(), + Data: tx.Data(), + GasLimit: tx.Gas(), + } + + v, r, s := tx.RawSignatureValues() + if to := tx.To(); to != nil { + txData.To = to.Hex() + } + + if tx.Value() != nil { + amountInt, err := eth.SafeNewIntFromBigInt(tx.Value()) + if err != nil { + return nil, err + } + txData.Amount = &amountInt + } + + if tx.GasPrice() != nil { + gasPriceInt, err := eth.SafeNewIntFromBigInt(tx.GasPrice()) + if err != nil { + return nil, err + } + txData.GasPrice = &gasPriceInt + } + + if tx.AccessList() != nil { + al := tx.AccessList() + txData.Accesses = NewAccessList(&al) + } + + txData.SetSignatureValues(tx.ChainId(), v, r, s) + return txData, nil +} + +// TxType returns a byte (uint8) identifying the tx as a [LegacyTx] (0), +// [AccessListTx] (1), or [DynamicFeeTx] (2). +func (tx *AccessListTx) TxType() uint8 { + return gethcore.AccessListTxType +} + +// Copy returns an instance with the same field values +func (tx *AccessListTx) Copy() TxData { + return &AccessListTx{ + ChainID: tx.ChainID, + Nonce: tx.Nonce, + GasPrice: tx.GasPrice, + GasLimit: tx.GasLimit, + To: tx.To, + Amount: tx.Amount, + Data: common.CopyBytes(tx.Data), + Accesses: tx.Accesses, + V: common.CopyBytes(tx.V), + R: common.CopyBytes(tx.R), + S: common.CopyBytes(tx.S), + } +} + +// GetChainID returns the chain id field from the AccessListTx +func (tx *AccessListTx) GetChainID() *big.Int { + if tx.ChainID == nil { + return nil + } + + return tx.ChainID.BigInt() +} + +// GetAccessList returns the AccessList field. +func (tx *AccessListTx) GetAccessList() gethcore.AccessList { + if tx.Accesses == nil { + return nil + } + return *tx.Accesses.ToEthAccessList() +} + +// GetData returns a copy of the input data bytes. +func (tx *AccessListTx) GetData() []byte { + return common.CopyBytes(tx.Data) +} + +// GetGas returns the gas limit. +func (tx *AccessListTx) GetGas() uint64 { + return tx.GasLimit +} + +// Gas price as wei spent per unit gas. +func (tx *AccessListTx) GetGasPrice() *big.Int { + if tx.GasPrice == nil { + return nil + } + return tx.GasPrice.BigInt() +} + +// GetGasTipCapWei returns a cap on the gas tip in units of wei. +// For an [AccessListTx], this is taken to be the gas price. +// +// Also called "maxPriorityFeePerGas" in Alchemy and Ethers. +// See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]. +// Base fees are determined by the network, not the end user that broadcasts +// the transaction. Adding a tip increases one's "priority" in the block. +// +// The terminology "fee per gas" essentially means "wei per unit gas". +// See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas] for more info. +// +// [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]: https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas. +func (tx *AccessListTx) GetGasTipCapWei() *big.Int { + return tx.GetGasPrice() +} + +// GetGasFeeCapWei returns a cap on the gas fees paid in units of wei: +// For an [AccessListTx], this is taken to be the gas price. +// +// The terminology "fee per gas" essentially means "wei per unit gas". +// See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas] for more info. +// +// [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]: https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas. +func (tx *AccessListTx) GetGasFeeCapWei() *big.Int { + return tx.GetGasPrice() +} + +func (tx *AccessListTx) EffectiveGasFeeCapWei(baseFeeWei *big.Int) *big.Int { + return BigIntMax(baseFeeWei, tx.GetGasFeeCapWei()) +} + +// GetValueWei returns the tx amount. +func (tx *AccessListTx) GetValueWei() *big.Int { + if tx.Amount == nil { + return nil + } + + return tx.Amount.BigInt() +} + +// GetNonce returns the account sequence for the transaction. +func (tx *AccessListTx) GetNonce() uint64 { return tx.Nonce } + +// GetTo returns the pointer to the recipient address. +func (tx *AccessListTx) GetTo() *common.Address { + if tx.To == "" { + return nil + } + to := common.HexToAddress(tx.To) + return &to +} + +// AsEthereumData returns an AccessListTx transaction tx from the proto-formatted +// TxData defined on the Cosmos EVM. +func (tx *AccessListTx) AsEthereumData() gethcore.TxData { + v, r, s := tx.GetRawSignatureValues() + return &gethcore.AccessListTx{ + ChainID: tx.GetChainID(), + Nonce: tx.GetNonce(), + GasPrice: tx.GetGasPrice(), + Gas: tx.GetGas(), + To: tx.GetTo(), + Value: tx.GetValueWei(), + Data: tx.GetData(), + AccessList: tx.GetAccessList(), + V: v, + R: r, + S: s, + } +} + +// GetRawSignatureValues returns the V, R, S signature values of the transaction. +// The return values should not be modified by the caller. +func (tx *AccessListTx) GetRawSignatureValues() (v, r, s *big.Int) { + return rawSignatureValues(tx.V, tx.R, tx.S) +} + +// SetSignatureValues sets the signature values to the transaction. +func (tx *AccessListTx) SetSignatureValues(chainID, v, r, s *big.Int) { + if v != nil { + tx.V = v.Bytes() + } + if r != nil { + tx.R = r.Bytes() + } + if s != nil { + tx.S = s.Bytes() + } + if chainID != nil { + chainIDInt := sdkmath.NewIntFromBigInt(chainID) + tx.ChainID = &chainIDInt + } +} + +// Validate performs a stateless validation of the tx fields. +func (tx AccessListTx) Validate() error { + for _, err := range []error{ + ValidateTxDataAmount(&tx), + ValidateTxDataTo(&tx), + ValidateTxDataGasPrice(&tx), + } { + if err != nil { + return err + } + } + + if !eth.IsValidInt256(tx.Fee()) { + return errorsmod.Wrap(ErrInvalidGasFee, "out of bound") + } + + chainID := tx.GetChainID() + + if chainID == nil { + return errorsmod.Wrap( + errortypes.ErrInvalidChainID, + "chain ID must be present on AccessList txs", + ) + } + + return nil +} + +// Fee returns gasPrice * gasLimit. +func (tx AccessListTx) Fee() *big.Int { + return priceTimesGas(tx.GetGasPrice(), tx.GetGas()) +} + +// Cost returns amount + gasPrice * gasLimit. +func (tx AccessListTx) Cost() *big.Int { + return cost(tx.Fee(), tx.GetValueWei()) +} + +// EffectiveGasPriceWeiPerGas is the same as GasPrice for AccessListTx +func (tx AccessListTx) EffectiveGasPriceWeiPerGas(baseFeeWei *big.Int) *big.Int { + return BigIntMax(tx.GetGasPrice(), baseFeeWei) +} + +// EffectiveFeeWei is the same as Fee for AccessListTx +func (tx AccessListTx) EffectiveFeeWei(baseFeeWei *big.Int) *big.Int { + return priceTimesGas(tx.EffectiveGasPriceWeiPerGas(baseFeeWei), tx.GetGas()) +} + +// EffectiveCostWei is the same as Cost for AccessListTx +func (tx AccessListTx) EffectiveCostWei(baseFeeWei *big.Int) *big.Int { + return cost( + tx.EffectiveFeeWei(baseFeeWei), tx.GetValueWei(), + ) +} diff --git a/x/evm/tx_data_access_list_test.go b/x/evm/tx_data_access_list_test.go new file mode 100644 index 000000000..54b0760dd --- /dev/null +++ b/x/evm/tx_data_access_list_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm_test + +import ( + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +func (suite *Suite) TestTestNewAccessList() { + testCases := []struct { + name string + ethAccessList *gethcore.AccessList + expAl evm.AccessList + }{ + { + "ethAccessList is nil", + nil, + nil, + }, + { + "non-empty ethAccessList", + &gethcore.AccessList{{Address: suite.addr, StorageKeys: []common.Hash{{0}}}}, + evm.AccessList{{Address: suite.hexAddr, StorageKeys: []string{common.Hash{}.Hex()}}}, + }, + } + for _, tc := range testCases { + al := evm.NewAccessList(tc.ethAccessList) + + suite.Require().Equal(tc.expAl, al) + } +} + +func (suite *Suite) TestAccessListToEthAccessList() { + ethAccessList := gethcore.AccessList{{Address: suite.addr, StorageKeys: []common.Hash{{0}}}} + al := evm.NewAccessList(ðAccessList) + actual := al.ToEthAccessList() + + suite.Require().Equal(ðAccessList, actual) +} diff --git a/x/evm/tx_data_dynamic_fee.go b/x/evm/tx_data_dynamic_fee.go new file mode 100644 index 000000000..040fa5d4d --- /dev/null +++ b/x/evm/tx_data_dynamic_fee.go @@ -0,0 +1,317 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + gethmath "github.com/ethereum/go-ethereum/common/math" + gethcore "github.com/ethereum/go-ethereum/core/types" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +// BigIntMax returns max(x,y). +func BigIntMax(x, y *big.Int) *big.Int { + if x == nil && y != nil { + return y + } else if x != nil && y == nil { + return x + } else if x == nil && y == nil { + return nil + } + + if x.Cmp(y) > 0 { + return x + } + return y +} + +func NewDynamicFeeTx(tx *gethcore.Transaction) (*DynamicFeeTx, error) { + txData := &DynamicFeeTx{ + Nonce: tx.Nonce(), + Data: tx.Data(), + GasLimit: tx.Gas(), + } + + v, r, s := tx.RawSignatureValues() + if to := tx.To(); to != nil { + txData.To = to.Hex() + } + + if tx.Value() != nil { + amountInt, err := eth.SafeNewIntFromBigInt(tx.Value()) + if err != nil { + return nil, err + } + txData.Amount = &amountInt + } + + if tx.GasFeeCap() != nil { + gasFeeCapInt, err := eth.SafeNewIntFromBigInt(tx.GasFeeCap()) + if err != nil { + return nil, err + } + txData.GasFeeCap = &gasFeeCapInt + } + + if tx.GasTipCap() != nil { + gasTipCapInt, err := eth.SafeNewIntFromBigInt(tx.GasTipCap()) + if err != nil { + return nil, err + } + txData.GasTipCap = &gasTipCapInt + } + + if tx.AccessList() != nil { + al := tx.AccessList() + txData.Accesses = NewAccessList(&al) + } + + txData.SetSignatureValues(tx.ChainId(), v, r, s) + return txData, nil +} + +// TxType returns a byte (uint8) identifying the tx as a [LegacyTx] (0), +// [AccessListTx] (1), or [DynamicFeeTx] (2). +func (tx *DynamicFeeTx) TxType() uint8 { + return gethcore.DynamicFeeTxType +} + +// Copy returns an instance with the same field values +func (tx *DynamicFeeTx) Copy() TxData { + return &DynamicFeeTx{ + ChainID: tx.ChainID, + Nonce: tx.Nonce, + GasTipCap: tx.GasTipCap, + GasFeeCap: tx.GasFeeCap, + GasLimit: tx.GasLimit, + To: tx.To, + Amount: tx.Amount, + Data: common.CopyBytes(tx.Data), + Accesses: tx.Accesses, + V: common.CopyBytes(tx.V), + R: common.CopyBytes(tx.R), + S: common.CopyBytes(tx.S), + } +} + +// GetChainID returns the chain id field from the DynamicFeeTx +func (tx *DynamicFeeTx) GetChainID() *big.Int { + if tx.ChainID == nil { + return nil + } + + return tx.ChainID.BigInt() +} + +// GetAccessList returns the AccessList field. +func (tx *DynamicFeeTx) GetAccessList() gethcore.AccessList { + if tx.Accesses == nil { + return nil + } + return *tx.Accesses.ToEthAccessList() +} + +// GetData returns a copy of the input data bytes. +func (tx *DynamicFeeTx) GetData() []byte { + return common.CopyBytes(tx.Data) +} + +// GetGas returns the gas limit. +func (tx *DynamicFeeTx) GetGas() uint64 { + return tx.GasLimit +} + +// Gas price as wei spent per unit gas. +func (tx *DynamicFeeTx) GetGasPrice() *big.Int { + return tx.GetGasFeeCapWei() +} + +// GetGasTipCapWei returns a cap on the gas tip in units of wei. +// +// Also called "maxPriorityFeePerGas" in Alchemy and Ethers. +// See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]. +// Base fees are determined by the network, not the end user that broadcasts +// the transaction. Adding a tip increases one's "priority" in the block. +// +// The terminology "fee per gas" essentially means "wei per unit gas". +// See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas] for more info. +// +// [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]: https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas. +func (tx *DynamicFeeTx) GetGasTipCapWei() *big.Int { + if tx.GasTipCap == nil { + return nil + } + return tx.GasTipCap.BigInt() +} + +// GetGasFeeCapWei returns a cap on the gas fees paid in units of wei, where: +// +// feesWithoutCap := effective gas price (wei per gas) * gas units +// gas fee cap -> min(feesWithoutCap, gasFeeCap) +// +// Also called "maxFeePerGas" in Alchemy and Ethers. +// maxFeePerGas := baseFeePerGas + maxPriorityFeePerGas +// +// The terminology "fee per gas" essentially means "wei per unit gas". +// See [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas] for more info. +// +// [Alchemy Docs - maxPriorityFeePerGas vs maxFeePerGas]: https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas. +func (tx *DynamicFeeTx) GetGasFeeCapWei() *big.Int { + if tx.GasFeeCap == nil { + return nil + } + return tx.GasFeeCap.BigInt() +} + +func (tx *DynamicFeeTx) EffectiveGasFeeCapWei(baseFeeWei *big.Int) *big.Int { + return BigIntMax(baseFeeWei, tx.GetGasFeeCapWei()) +} + +// GetValueWei returns the tx amount. +func (tx *DynamicFeeTx) GetValueWei() *big.Int { + if tx.Amount == nil { + return nil + } + + return tx.Amount.BigInt() +} + +// GetNonce returns the account sequence for the transaction. +func (tx *DynamicFeeTx) GetNonce() uint64 { return tx.Nonce } + +// GetTo returns the pointer to the recipient address. +func (tx *DynamicFeeTx) GetTo() *common.Address { + if tx.To == "" { + return nil + } + to := common.HexToAddress(tx.To) + return &to +} + +// AsEthereumData returns an DynamicFeeTx transaction tx from the proto-formatted +// TxData defined on the Cosmos EVM. +func (tx *DynamicFeeTx) AsEthereumData() gethcore.TxData { + v, r, s := tx.GetRawSignatureValues() + return &gethcore.DynamicFeeTx{ + ChainID: tx.GetChainID(), + Nonce: tx.GetNonce(), + GasTipCap: tx.GetGasTipCapWei(), + GasFeeCap: tx.GetGasFeeCapWei(), + Gas: tx.GetGas(), + To: tx.GetTo(), + Value: tx.GetValueWei(), + Data: tx.GetData(), + AccessList: tx.GetAccessList(), + V: v, + R: r, + S: s, + } +} + +// GetRawSignatureValues returns the V, R, S signature values of the transaction. +// The return values should not be modified by the caller. +func (tx *DynamicFeeTx) GetRawSignatureValues() (v, r, s *big.Int) { + return rawSignatureValues(tx.V, tx.R, tx.S) +} + +// SetSignatureValues sets the signature values to the transaction. +func (tx *DynamicFeeTx) SetSignatureValues(chainID, v, r, s *big.Int) { + if v != nil { + tx.V = v.Bytes() + } + if r != nil { + tx.R = r.Bytes() + } + if s != nil { + tx.S = s.Bytes() + } + if chainID != nil { + chainIDInt := sdkmath.NewIntFromBigInt(chainID) + tx.ChainID = &chainIDInt + } +} + +// Validate performs a stateless validation of the tx fields. +func (tx DynamicFeeTx) Validate() error { + if tx.GasTipCap == nil { + return errorsmod.Wrap(ErrInvalidGasCap, "gas tip cap cannot nil") + } + + if tx.GasFeeCap == nil { + return errorsmod.Wrap(ErrInvalidGasCap, "gas fee cap cannot nil") + } + + if tx.GasTipCap.IsNegative() { + return errorsmod.Wrapf(ErrInvalidGasCap, "gas tip cap cannot be negative %s", tx.GasTipCap) + } + + if tx.GasFeeCap.IsNegative() { + return errorsmod.Wrapf(ErrInvalidGasCap, "gas fee cap cannot be negative %s", tx.GasFeeCap) + } + + if !eth.IsValidInt256(tx.GetGasTipCapWei()) { + return errorsmod.Wrap(ErrInvalidGasCap, "out of bound") + } + + if !eth.IsValidInt256(tx.GetGasFeeCapWei()) { + return errorsmod.Wrap(ErrInvalidGasCap, "out of bound") + } + + if tx.GasFeeCap.LT(*tx.GasTipCap) { + return errorsmod.Wrapf( + ErrInvalidGasCap, "max priority fee per gas higher than max fee per gas (%s > %s)", + tx.GasTipCap, tx.GasFeeCap, + ) + } + + if !eth.IsValidInt256(tx.Fee()) { + return errorsmod.Wrap(ErrInvalidGasFee, "out of bound") + } + + for _, err := range []error{ + ValidateTxDataAmount(&tx), + ValidateTxDataTo(&tx), + ValidateTxDataChainID(&tx), + } { + if err != nil { + return err + } + } + + return nil +} + +// Fee returns gasPrice * gasLimit. +func (tx DynamicFeeTx) Fee() *big.Int { + return priceTimesGas(tx.GetGasFeeCapWei(), tx.GasLimit) +} + +// Cost returns amount + gasPrice * gasLimit. +func (tx DynamicFeeTx) Cost() *big.Int { + return cost(tx.Fee(), tx.GetValueWei()) +} + +// EffectiveGasPriceWeiPerGas returns the effective gas price based on EIP-1559 rules. +// `effectiveGasPrice = min(baseFee + tipCap, feeCap)` +func (tx *DynamicFeeTx) EffectiveGasPriceWeiPerGas(baseFeeWei *big.Int) *big.Int { + feeWithSpecifiedTip := new(big.Int).Add(tx.GasTipCap.BigInt(), baseFeeWei) + + // Enforce base fee as the minimum [EffectiveGasPriceWei]: + rawEffectiveGasPrice := gethmath.BigMin(feeWithSpecifiedTip, tx.GasFeeCap.BigInt()) + return BigIntMax(baseFeeWei, rawEffectiveGasPrice) +} + +// EffectiveFeeWei returns effective_gasPrice * gasLimit. +func (tx DynamicFeeTx) EffectiveFeeWei(baseFeeWei *big.Int) *big.Int { + return priceTimesGas(tx.EffectiveGasPriceWeiPerGas(baseFeeWei), tx.GasLimit) +} + +// EffectiveCostWei returns amount + effective_gasPrice * gasLimit. +func (tx DynamicFeeTx) EffectiveCostWei(baseFeeWei *big.Int) *big.Int { + return cost(tx.EffectiveFeeWei(baseFeeWei), tx.GetValueWei()) +} diff --git a/x/evm/tx_data_dynamic_fee_test.go b/x/evm/tx_data_dynamic_fee_test.go new file mode 100644 index 000000000..1136af0e2 --- /dev/null +++ b/x/evm/tx_data_dynamic_fee_test.go @@ -0,0 +1,676 @@ +package evm_test + +import ( + "math/big" + "strings" + + "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +func (suite *Suite) TestNewDynamicFeeTx() { + testCases := []struct { + name string + expError bool + tx *gethcore.Transaction + }{ + { + "non-empty tx", + false, + gethcore.NewTx(&gethcore.DynamicFeeTx{ + Nonce: 1, + Data: []byte("data"), + Gas: 100, + Value: big.NewInt(1), + AccessList: gethcore.AccessList{}, + To: &suite.addr, + V: suite.bigInt, + R: suite.bigInt, + S: suite.bigInt, + }), + }, + { + "value out of bounds tx", + true, + gethcore.NewTx(&gethcore.DynamicFeeTx{ + Nonce: 1, + Data: []byte("data"), + Gas: 100, + Value: suite.overflowBigInt, + AccessList: gethcore.AccessList{}, + To: &suite.addr, + V: suite.bigInt, + R: suite.bigInt, + S: suite.bigInt, + }), + }, + { + "gas fee cap out of bounds tx", + true, + gethcore.NewTx(&gethcore.DynamicFeeTx{ + Nonce: 1, + Data: []byte("data"), + Gas: 100, + GasFeeCap: suite.overflowBigInt, + Value: big.NewInt(1), + AccessList: gethcore.AccessList{}, + To: &suite.addr, + V: suite.bigInt, + R: suite.bigInt, + S: suite.bigInt, + }), + }, + { + "gas tip cap out of bounds tx", + true, + gethcore.NewTx(&gethcore.DynamicFeeTx{ + Nonce: 1, + Data: []byte("data"), + Gas: 100, + GasTipCap: suite.overflowBigInt, + Value: big.NewInt(1), + AccessList: gethcore.AccessList{}, + To: &suite.addr, + V: suite.bigInt, + R: suite.bigInt, + S: suite.bigInt, + }), + }, + } + for _, tc := range testCases { + tx, err := evm.NewDynamicFeeTx(tc.tx) + + if tc.expError { + suite.Require().Error(err) + } else { + suite.Require().NoError(err) + suite.Require().NotEmpty(tx) + suite.Require().Equal(uint8(2), tx.TxType()) + } + } +} + +func (suite *Suite) TestDynamicFeeTxAsEthereumData() { + feeConfig := &gethcore.DynamicFeeTx{ + Nonce: 1, + Data: []byte("data"), + Gas: 100, + Value: big.NewInt(1), + AccessList: gethcore.AccessList{}, + To: &suite.addr, + V: suite.bigInt, + R: suite.bigInt, + S: suite.bigInt, + } + + tx := gethcore.NewTx(feeConfig) + + dynamicFeeTx, err := evm.NewDynamicFeeTx(tx) + suite.Require().NoError(err) + + res := dynamicFeeTx.AsEthereumData() + resTx := gethcore.NewTx(res) + + suite.Require().Equal(feeConfig.Nonce, resTx.Nonce()) + suite.Require().Equal(feeConfig.Data, resTx.Data()) + suite.Require().Equal(feeConfig.Gas, resTx.Gas()) + suite.Require().Equal(feeConfig.Value, resTx.Value()) + suite.Require().Equal(feeConfig.AccessList, resTx.AccessList()) + suite.Require().Equal(feeConfig.To, resTx.To()) +} + +func (suite *Suite) TestDynamicFeeTxCopy() { + tx := &evm.DynamicFeeTx{} + txCopy := tx.Copy() + + suite.Require().Equal(&evm.DynamicFeeTx{}, txCopy) + // TODO: Test for different pointers +} + +func (suite *Suite) TestDynamicFeeTxGetChainID() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp *big.Int + }{ + { + "empty chainID", + evm.DynamicFeeTx{ + ChainID: nil, + }, + nil, + }, + { + "non-empty chainID", + evm.DynamicFeeTx{ + ChainID: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetChainID() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetAccessList() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp gethcore.AccessList + }{ + { + "empty accesses", + evm.DynamicFeeTx{ + Accesses: nil, + }, + nil, + }, + { + "nil", + evm.DynamicFeeTx{ + Accesses: evm.NewAccessList(nil), + }, + nil, + }, + { + "non-empty accesses", + evm.DynamicFeeTx{ + Accesses: evm.AccessList{ + { + Address: suite.hexAddr, + StorageKeys: []string{}, + }, + }, + }, + gethcore.AccessList{ + { + Address: suite.addr, + StorageKeys: []common.Hash{}, + }, + }, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetAccessList() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetData() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + }{ + { + "non-empty transaction", + evm.DynamicFeeTx{ + Data: nil, + }, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetData() + + suite.Require().Equal(tc.tx.Data, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetGas() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp uint64 + }{ + { + "non-empty gas", + evm.DynamicFeeTx{ + GasLimit: suite.uint64, + }, + suite.uint64, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGas() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetGasPrice() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp *big.Int + }{ + { + "non-empty gasFeeCap", + evm.DynamicFeeTx{ + GasFeeCap: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasPrice() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetGasTipCap() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp *big.Int + }{ + { + "empty gasTipCap", + evm.DynamicFeeTx{ + GasTipCap: nil, + }, + nil, + }, + { + "non-empty gasTipCap", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasTipCapWei() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetGasFeeCap() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp *big.Int + }{ + { + "empty gasFeeCap", + evm.DynamicFeeTx{ + GasFeeCap: nil, + }, + nil, + }, + { + "non-empty gasFeeCap", + evm.DynamicFeeTx{ + GasFeeCap: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasFeeCapWei() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetValue() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp *big.Int + }{ + { + "empty amount", + evm.DynamicFeeTx{ + Amount: nil, + }, + nil, + }, + { + "non-empty amount", + evm.DynamicFeeTx{ + Amount: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetValueWei() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetNonce() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp uint64 + }{ + { + "non-empty nonce", + evm.DynamicFeeTx{ + Nonce: suite.uint64, + }, + suite.uint64, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetNonce() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxGetTo() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + exp *common.Address + }{ + { + "empty suite.address", + evm.DynamicFeeTx{ + To: "", + }, + nil, + }, + { + "non-empty suite.address", + evm.DynamicFeeTx{ + To: suite.hexAddr, + }, + &suite.addr, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetTo() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxSetSignatureValues() { + testCases := []struct { + name string + chainID *big.Int + r *big.Int + v *big.Int + s *big.Int + }{ + { + "empty values", + nil, + nil, + nil, + nil, + }, + { + "non-empty values", + suite.bigInt, + suite.bigInt, + suite.bigInt, + suite.bigInt, + }, + } + + for _, tc := range testCases { + tx := &evm.DynamicFeeTx{} + tx.SetSignatureValues(tc.chainID, tc.v, tc.r, tc.s) + + v, r, s := tx.GetRawSignatureValues() + chainID := tx.GetChainID() + + suite.Require().Equal(tc.v, v, tc.name) + suite.Require().Equal(tc.r, r, tc.name) + suite.Require().Equal(tc.s, s, tc.name) + suite.Require().Equal(tc.chainID, chainID, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxValidate() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + expError bool + }{ + { + "empty", + evm.DynamicFeeTx{}, + true, + }, + { + "gas tip cap is nil", + evm.DynamicFeeTx{ + GasTipCap: nil, + }, + true, + }, + { + "gas fee cap is nil", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkZeroInt, + }, + true, + }, + { + "gas tip cap is negative", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkMinusOneInt, + GasFeeCap: &suite.sdkZeroInt, + }, + true, + }, + { + "gas tip cap is negative", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkZeroInt, + GasFeeCap: &suite.sdkMinusOneInt, + }, + true, + }, + { + "gas fee cap < gas tip cap", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkZeroInt, + }, + true, + }, + { + "amount is negative", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + Amount: &suite.sdkMinusOneInt, + }, + true, + }, + { + "to suite.address is invalid", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + Amount: &suite.sdkInt, + To: suite.invalidAddr, + }, + true, + }, + { + "chain ID not present on AccessList txs", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + Amount: &suite.sdkInt, + To: suite.hexAddr, + ChainID: nil, + }, + true, + }, + { + "no errors", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + Amount: &suite.sdkInt, + To: suite.hexAddr, + ChainID: &suite.sdkInt, + }, + false, + }, + } + + for _, tc := range testCases { + err := tc.tx.Validate() + + if tc.expError { + suite.Require().Error(err, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxEffectiveGasPrice() { + testCases := []struct { + name string + tx func() evm.DynamicFeeTx + baseFeeWei *big.Int + exp *big.Int + }{ + { + name: "all equal to base fee", + tx: func() evm.DynamicFeeTx { + return evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + } + }, + baseFeeWei: (&suite.sdkInt).BigInt(), + exp: (&suite.sdkInt).BigInt(), + }, + { + name: "baseFee < tip < feeCap", + tx: func() evm.DynamicFeeTx { + gasTipCap, _ := math.NewIntFromString("5" + strings.Repeat("0", 12)) + gasFeeCap, _ := math.NewIntFromString("10" + strings.Repeat("0", 12)) + return evm.DynamicFeeTx{ + GasTipCap: &gasTipCap, + GasFeeCap: &gasFeeCap, + } + }, + baseFeeWei: evm.BASE_FEE_WEI, + exp: evm.NativeToWei(big.NewInt(6)), + }, + { + name: "baseFee < feeCap < tip", + tx: func() evm.DynamicFeeTx { + gasTipCap, _ := math.NewIntFromString("10" + strings.Repeat("0", 12)) + gasFeeCap, _ := math.NewIntFromString("2" + strings.Repeat("0", 12)) + return evm.DynamicFeeTx{ + GasTipCap: &gasTipCap, + GasFeeCap: &gasFeeCap, + } + }, + baseFeeWei: evm.BASE_FEE_WEI, + exp: evm.NativeToWei(big.NewInt(2)), + }, + { + name: "below baseFee", + tx: func() evm.DynamicFeeTx { + gasTipCap, _ := math.NewIntFromString("0" + strings.Repeat("0", 12)) + gasFeeCap, _ := math.NewIntFromString("0" + strings.Repeat("0", 12)) + return evm.DynamicFeeTx{ + GasTipCap: &gasTipCap, + GasFeeCap: &gasFeeCap, + } + }, + baseFeeWei: evm.BASE_FEE_WEI, + exp: evm.NativeToWei(big.NewInt(1)), + }, + } + + for _, tc := range testCases { + txData := tc.tx() + actual := txData.EffectiveGasPriceWeiPerGas(tc.baseFeeWei) + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxEffectiveFee() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + baseFee *big.Int + exp *big.Int + }{ + { + "non-empty dynamic fee tx", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + GasLimit: uint64(1), + }, + (&suite.sdkInt).BigInt(), + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.EffectiveFeeWei(tc.baseFee) + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxEffectiveCost() { + testCases := []struct { + name string + tx evm.DynamicFeeTx + baseFee *big.Int + exp *big.Int + }{ + { + "non-empty dynamic fee tx", + evm.DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + GasLimit: uint64(1), + Amount: &suite.sdkZeroInt, + }, + (&suite.sdkInt).BigInt(), + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.EffectiveCostWei(tc.baseFee) + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestDynamicFeeTxFeeCost() { + tx := &evm.DynamicFeeTx{} + suite.Require().Panics(func() { tx.Fee() }, "should panic") + suite.Require().Panics(func() { tx.Cost() }, "should panic") +} diff --git a/x/evm/tx_data_legacy.go b/x/evm/tx_data_legacy.go new file mode 100644 index 000000000..1b3bab940 --- /dev/null +++ b/x/evm/tx_data_legacy.go @@ -0,0 +1,213 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "math/big" + + errorsmod "cosmossdk.io/errors" + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/eth" +) + +func NewLegacyTx(tx *gethcore.Transaction) (*LegacyTx, error) { + txData := &LegacyTx{ + Nonce: tx.Nonce(), + Data: tx.Data(), + GasLimit: tx.Gas(), + } + + v, r, s := tx.RawSignatureValues() + if to := tx.To(); to != nil { + txData.To = to.Hex() + } + + if tx.Value() != nil { + amountInt, err := eth.SafeNewIntFromBigInt(tx.Value()) + if err != nil { + return nil, err + } + txData.Amount = &amountInt + } + + if tx.GasPrice() != nil { + gasPriceInt, err := eth.SafeNewIntFromBigInt(tx.GasPrice()) + if err != nil { + return nil, err + } + txData.GasPrice = &gasPriceInt + } + + txData.SetSignatureValues(tx.ChainId(), v, r, s) + return txData, nil +} + +// TxType returns a byte (uint8) identifying the tx as a [LegacyTx] (0), +// [AccessListTx] (1), or [DynamicFeeTx] (2). +func (tx *LegacyTx) TxType() uint8 { + return gethcore.LegacyTxType +} + +// Copy returns an instance with the same field values +func (tx *LegacyTx) Copy() TxData { + return &LegacyTx{ + Nonce: tx.Nonce, + GasPrice: tx.GasPrice, + GasLimit: tx.GasLimit, + To: tx.To, + Amount: tx.Amount, + Data: common.CopyBytes(tx.Data), + V: common.CopyBytes(tx.V), + R: common.CopyBytes(tx.R), + S: common.CopyBytes(tx.S), + } +} + +// GetChainID returns the chain id field from the derived signature values +func (tx *LegacyTx) GetChainID() *big.Int { + v, _, _ := tx.GetRawSignatureValues() + return DeriveChainID(v) +} + +// GetAccessList returns nil +func (tx *LegacyTx) GetAccessList() gethcore.AccessList { + return nil +} + +// GetData returns a copy of the input data bytes. +func (tx *LegacyTx) GetData() []byte { + return common.CopyBytes(tx.Data) +} + +// GetGas returns the gas limit. +func (tx *LegacyTx) GetGas() uint64 { + return tx.GasLimit +} + +// GetGasPrice is equivalent to wei per unit gas. +func (tx *LegacyTx) GetGasPrice() *big.Int { + if tx.GasPrice == nil { + return nil + } + return tx.GasPrice.BigInt() +} + +// GetGasTipCapWei returns a cap on the gas tip in units of wei. +// For a [LegacyTx], this is taken to be the gas price. +func (tx *LegacyTx) GetGasTipCapWei() *big.Int { + return tx.GetGasPrice() +} + +// GetGasFeeCapWei returns a cap on the gas fees paid in units of wei. +// For a [LegacyTx], this is taken to be the gas price. +func (tx *LegacyTx) GetGasFeeCapWei() *big.Int { + return tx.GetGasPrice() +} + +func (tx *LegacyTx) EffectiveGasFeeCapWei(baseFeeWei *big.Int) *big.Int { + return BigIntMax(baseFeeWei, tx.GetGasFeeCapWei()) +} + +// GetValueWei returns the tx amount. +func (tx *LegacyTx) GetValueWei() *big.Int { + if tx.Amount == nil { + return nil + } + return tx.Amount.BigInt() +} + +// GetNonce returns the account sequence for the transaction. +func (tx *LegacyTx) GetNonce() uint64 { return tx.Nonce } + +// GetTo returns the pointer to the recipient address. +func (tx *LegacyTx) GetTo() *common.Address { + if tx.To == "" { + return nil + } + to := common.HexToAddress(tx.To) + return &to +} + +// AsEthereumData returns an LegacyTx transaction tx from the proto-formatted +// TxData defined on the Cosmos EVM. +func (tx *LegacyTx) AsEthereumData() gethcore.TxData { + v, r, s := tx.GetRawSignatureValues() + return &gethcore.LegacyTx{ + Nonce: tx.GetNonce(), + GasPrice: tx.GetGasPrice(), + Gas: tx.GetGas(), + To: tx.GetTo(), + Value: tx.GetValueWei(), + Data: tx.GetData(), + V: v, + R: r, + S: s, + } +} + +// GetRawSignatureValues returns the V, R, S signature values of the transaction. +// The return values should not be modified by the caller. +func (tx *LegacyTx) GetRawSignatureValues() (v, r, s *big.Int) { + return rawSignatureValues(tx.V, tx.R, tx.S) +} + +// SetSignatureValues sets the signature values to the transaction. +func (tx *LegacyTx) SetSignatureValues(_, v, r, s *big.Int) { + if v != nil { + tx.V = v.Bytes() + } + if r != nil { + tx.R = r.Bytes() + } + if s != nil { + tx.S = s.Bytes() + } +} + +// Validate performs a stateless validation of the tx fields. +func (tx LegacyTx) Validate() error { + for _, err := range []error{ + ValidateTxDataAmount(&tx), + ValidateTxDataTo(&tx), + ValidateTxDataGasPrice(&tx), + ValidateTxDataChainID(&tx), + } { + if err != nil { + return err + } + } + + if !eth.IsValidInt256(tx.Fee()) { + return errorsmod.Wrap(ErrInvalidGasFee, "out of bound") + } + + return nil +} + +// Fee := gasPrice (wei per gas) * gasLimit (gas). Thus, fee is in units of +// wei. +func (tx LegacyTx) Fee() *big.Int { + return priceTimesGas(tx.GetGasPrice(), tx.GetGas()) +} + +// Cost returns amount + gasPrice * gasLimit. +func (tx LegacyTx) Cost() *big.Int { + return cost(tx.Fee(), tx.GetValueWei()) +} + +// EffectiveGasPriceWeiPerGas is the same as GasPrice for LegacyTx +func (tx LegacyTx) EffectiveGasPriceWeiPerGas(baseFeeWei *big.Int) *big.Int { + return BigIntMax(tx.GetGasPrice(), baseFeeWei) +} + +// EffectiveFeeWei is the same as Fee for LegacyTx +func (tx LegacyTx) EffectiveFeeWei(baseFeeWei *big.Int) *big.Int { + return priceTimesGas(tx.EffectiveGasPriceWeiPerGas(baseFeeWei), tx.GetGas()) +} + +// EffectiveCostWei is the same as Cost for LegacyTx +func (tx LegacyTx) EffectiveCostWei(baseFeeWei *big.Int) *big.Int { + txFee := tx.EffectiveFeeWei(baseFeeWei) + return cost(txFee, tx.GetValueWei()) +} diff --git a/x/evm/tx_data_legacy_test.go b/x/evm/tx_data_legacy_test.go new file mode 100644 index 000000000..ae4da41ba --- /dev/null +++ b/x/evm/tx_data_legacy_test.go @@ -0,0 +1,437 @@ +package evm_test + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + gethcore "github.com/ethereum/go-ethereum/core/types" + + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (suite *Suite) TestNewLegacyTx() { + testCases := []struct { + name string + tx *gethcore.Transaction + }{ + { + "non-empty Transaction", + gethcore.NewTx(&gethcore.AccessListTx{ + Nonce: 1, + Data: []byte("data"), + Gas: 100, + Value: big.NewInt(1), + AccessList: gethcore.AccessList{}, + To: &suite.addr, + V: big.NewInt(1), + R: big.NewInt(1), + S: big.NewInt(1), + }), + }, + } + + for _, tc := range testCases { + tx, err := evm.NewLegacyTx(tc.tx) + suite.Require().NoError(err) + + suite.Require().NotEmpty(tc.tx) + suite.Require().Equal(uint8(0), tx.TxType()) + } +} + +func (suite *Suite) TestLegacyTxTxType() { + tx := evm.LegacyTx{} + actual := tx.TxType() + + suite.Require().Equal(uint8(0), actual) +} + +func (suite *Suite) TestLegacyTxCopy() { + tx := &evm.LegacyTx{} + txData := tx.Copy() + + suite.Require().Equal(&evm.LegacyTx{}, txData) + // TODO: Test for different pointers +} + +func (suite *Suite) TestLegacyTxGetChainID() { + tx := evm.LegacyTx{} + actual := tx.GetChainID() + + suite.Require().Nil(actual) +} + +func (suite *Suite) TestLegacyTxGetAccessList() { + tx := evm.LegacyTx{} + actual := tx.GetAccessList() + + suite.Require().Nil(actual) +} + +func (suite *Suite) TestLegacyTxGetData() { + testCases := []struct { + name string + tx evm.LegacyTx + }{ + { + "non-empty transaction", + evm.LegacyTx{ + Data: nil, + }, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetData() + + suite.Require().Equal(tc.tx.Data, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxGetGas() { + testCases := []struct { + name string + tx evm.LegacyTx + exp uint64 + }{ + { + "non-empty gas", + evm.LegacyTx{ + GasLimit: suite.uint64, + }, + suite.uint64, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGas() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxGetGasPrice() { + testCases := []struct { + name string + tx evm.LegacyTx + exp *big.Int + }{ + { + "empty gasPrice", + evm.LegacyTx{ + GasPrice: nil, + }, + nil, + }, + { + "non-empty gasPrice", + evm.LegacyTx{ + GasPrice: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasFeeCapWei() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxGetGasTipCap() { + testCases := []struct { + name string + tx evm.LegacyTx + exp *big.Int + }{ + { + "non-empty gasPrice", + evm.LegacyTx{ + GasPrice: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasTipCapWei() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxGetGasFeeCap() { + testCases := []struct { + name string + tx evm.LegacyTx + exp *big.Int + }{ + { + "non-empty gasPrice", + evm.LegacyTx{ + GasPrice: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasFeeCapWei() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxGetValue() { + testCases := []struct { + name string + tx evm.LegacyTx + exp *big.Int + }{ + { + "empty amount", + evm.LegacyTx{ + Amount: nil, + }, + nil, + }, + { + "non-empty amount", + evm.LegacyTx{ + Amount: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetValueWei() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxGetNonce() { + testCases := []struct { + name string + tx evm.LegacyTx + exp uint64 + }{ + { + "none-empty nonce", + evm.LegacyTx{ + Nonce: suite.uint64, + }, + suite.uint64, + }, + } + for _, tc := range testCases { + actual := tc.tx.GetNonce() + + suite.Require().Equal(tc.exp, actual) + } +} + +func (suite *Suite) TestLegacyTxGetTo() { + testCases := []struct { + name string + tx evm.LegacyTx + exp *common.Address + }{ + { + "empty address", + evm.LegacyTx{ + To: "", + }, + nil, + }, + { + "non-empty address", + evm.LegacyTx{ + To: suite.hexAddr, + }, + &suite.addr, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetTo() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxAsEthereumData() { + tx := &evm.LegacyTx{} + txData := tx.AsEthereumData() + + suite.Require().Equal(&gethcore.LegacyTx{}, txData) +} + +func (suite *Suite) TestLegacyTxSetSignatureValues() { + testCases := []struct { + name string + v *big.Int + r *big.Int + s *big.Int + }{ + { + "non-empty values", + suite.bigInt, + suite.bigInt, + suite.bigInt, + }, + } + for _, tc := range testCases { + tx := &evm.LegacyTx{} + tx.SetSignatureValues(nil, tc.v, tc.r, tc.s) + + v, r, s := tx.GetRawSignatureValues() + + suite.Require().Equal(tc.v, v, tc.name) + suite.Require().Equal(tc.r, r, tc.name) + suite.Require().Equal(tc.s, s, tc.name) + } +} + +func (suite *Suite) TestLegacyTxValidate() { + testCases := []struct { + name string + tx func(tx *evm.LegacyTx) *evm.LegacyTx + expError bool + }{ + { + name: "empty", + tx: func(_ *evm.LegacyTx) *evm.LegacyTx { return new(evm.LegacyTx) }, + expError: true, + }, + { + name: "gas price is nil", + tx: func(tx *evm.LegacyTx) *evm.LegacyTx { + tx.GasPrice = nil + return tx + }, + expError: true, + }, + { + name: "gas price is negative", + tx: func(tx *evm.LegacyTx) *evm.LegacyTx { + tx.GasPrice = &suite.sdkMinusOneInt + return tx + }, + expError: true, + }, + { + name: "amount is negative", + tx: func(tx *evm.LegacyTx) *evm.LegacyTx { + tx.Amount = &suite.sdkMinusOneInt + return tx + }, + expError: true, + }, + { + name: "to address is invalid", + tx: func(tx *evm.LegacyTx) *evm.LegacyTx { + tx.To = suite.invalidAddr + return tx + }, + expError: true, + }, + } + + for _, tc := range testCases { + got := tc.tx(evmtest.ValidLegacyTx()) + err := got.Validate() + + if tc.expError { + suite.Require().Error(err, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + } +} + +func (suite *Suite) TestLegacyTxEffectiveGasPrice() { + testCases := []struct { + name string + tx evm.LegacyTx + baseFee *big.Int + exp *big.Int + }{ + { + "non-empty legacy tx", + evm.LegacyTx{ + GasPrice: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.EffectiveGasPriceWeiPerGas(tc.baseFee) + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxEffectiveFee() { + testCases := []struct { + name string + tx evm.LegacyTx + baseFee *big.Int + exp *big.Int + }{ + { + "non-empty legacy tx", + evm.LegacyTx{ + GasPrice: &suite.sdkInt, + GasLimit: uint64(1), + }, + (&suite.sdkInt).BigInt(), + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.EffectiveFeeWei(tc.baseFee) + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxEffectiveCost() { + testCases := []struct { + name string + tx evm.LegacyTx + baseFee *big.Int + exp *big.Int + }{ + { + "non-empty legacy tx", + evm.LegacyTx{ + GasPrice: &suite.sdkInt, + GasLimit: uint64(1), + Amount: &suite.sdkZeroInt, + }, + (&suite.sdkInt).BigInt(), + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.EffectiveCostWei(tc.baseFee) + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *Suite) TestLegacyTxFeeCost() { + tx := &evm.LegacyTx{} + + suite.Require().Panics(func() { tx.Fee() }, "should panic") + suite.Require().Panics(func() { tx.Cost() }, "should panic") +} diff --git a/x/evm/tx_data_test.go b/x/evm/tx_data_test.go new file mode 100644 index 000000000..ec2d893e1 --- /dev/null +++ b/x/evm/tx_data_test.go @@ -0,0 +1,89 @@ +package evm + +import ( + "math/big" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/require" +) + +func TestTxData_chainID(t *testing.T) { + chainID := sdkmath.NewInt(1) + + testCases := []struct { + msg string + data TxData + expChainID *big.Int + }{ + { + "access list tx", &AccessListTx{Accesses: AccessList{}, ChainID: &chainID}, big.NewInt(1), + }, + { + "access list tx, nil chain ID", &AccessListTx{Accesses: AccessList{}}, nil, + }, + { + "legacy tx, derived", &LegacyTx{}, nil, + }, + } + + for _, tc := range testCases { + chainID := tc.data.GetChainID() + require.Equal(t, chainID, tc.expChainID, tc.msg) + } +} + +func TestTxData_DeriveChainID(t *testing.T) { + bitLen64, ok := new(big.Int).SetString("0x8000000000000000", 0) + require.True(t, ok) + + bitLen80, ok := new(big.Int).SetString("0x80000000000000000000", 0) + require.True(t, ok) + + expBitLen80, ok := new(big.Int).SetString("302231454903657293676526", 0) + require.True(t, ok) + + testCases := []struct { + msg string + data TxData + expChainID *big.Int + }{ + { + "v = -1", &LegacyTx{V: big.NewInt(-1).Bytes()}, nil, + }, + { + "v = 0", &LegacyTx{V: big.NewInt(0).Bytes()}, nil, + }, + { + "v = 1", &LegacyTx{V: big.NewInt(1).Bytes()}, nil, + }, + { + "v = 27", &LegacyTx{V: big.NewInt(27).Bytes()}, new(big.Int), + }, + { + "v = 28", &LegacyTx{V: big.NewInt(28).Bytes()}, new(big.Int), + }, + { + "Ethereum mainnet", &LegacyTx{V: big.NewInt(37).Bytes()}, big.NewInt(1), + }, + { + "chain ID 9000", &LegacyTx{V: big.NewInt(18035).Bytes()}, big.NewInt(9000), + }, + { + "bit len 64", &LegacyTx{V: bitLen64.Bytes()}, big.NewInt(4611686018427387886), + }, + { + "bit len 80", &LegacyTx{V: bitLen80.Bytes()}, expBitLen80, + }, + { + "v = nil ", &LegacyTx{V: nil}, nil, + }, + } + + for _, tc := range testCases { + v, _, _ := tc.data.GetRawSignatureValues() + + chainID := DeriveChainID(v) + require.Equal(t, tc.expChainID, chainID, tc.msg) + } +} diff --git a/x/evm/tx_test.go b/x/evm/tx_test.go new file mode 100644 index 000000000..d7c884c8b --- /dev/null +++ b/x/evm/tx_test.go @@ -0,0 +1,55 @@ +package evm_test + +import ( + "math/big" + "testing" + + sdkmath "cosmossdk.io/math" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + + "github.com/stretchr/testify/suite" +) + +type Suite struct { + suite.Suite + + sdkInt sdkmath.Int + uint64 uint64 + hexUint64 hexutil.Uint64 + bigInt *big.Int + hexBigInt hexutil.Big + overflowBigInt *big.Int + sdkZeroInt sdkmath.Int + sdkMinusOneInt sdkmath.Int + invalidAddr string + addr common.Address + hexAddr string + hexDataBytes hexutil.Bytes + hexInputBytes hexutil.Bytes +} + +func (suite *Suite) SetupTest() { + suite.sdkInt = sdkmath.NewInt(9001) + suite.uint64 = suite.sdkInt.Uint64() + suite.hexUint64 = hexutil.Uint64(100) + suite.bigInt = big.NewInt(1) + suite.hexBigInt = hexutil.Big(*big.NewInt(1)) + suite.overflowBigInt = big.NewInt(0).Exp(big.NewInt(10), big.NewInt(256), nil) + suite.sdkZeroInt = sdkmath.ZeroInt() + suite.sdkMinusOneInt = sdkmath.NewInt(-1) + suite.invalidAddr = "123456" + + suite.addr = evmtest.NewEthPrivAcc().EthAddr + + suite.hexAddr = suite.addr.Hex() + suite.hexDataBytes = hexutil.Bytes([]byte("data")) + suite.hexInputBytes = hexutil.Bytes([]byte("input")) +} + +func TestTxDataTestSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} diff --git a/x/evm/vmtracer.go b/x/evm/vmtracer.go new file mode 100644 index 000000000..2078bdfe7 --- /dev/null +++ b/x/evm/vmtracer.go @@ -0,0 +1,116 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package evm + +import ( + "math/big" + "os" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/logger" + "github.com/ethereum/go-ethereum/params" +) + +const ( + TracerAccessList = "access_list" + TracerJSON = "json" + TracerStruct = "struct" + TracerMarkdown = "markdown" +) + +// NewTracer creates a new Logger tracer to collect execution traces from an +// EVM transaction. +func NewTracer(tracer string, msg core.Message, cfg *params.ChainConfig, height int64) vm.EVMLogger { + // TODO: enable additional log configuration + logCfg := &logger.Config{ + Debug: true, + } + + switch tracer { + case TracerAccessList: + rules := cfg.Rules(big.NewInt(height), cfg.MergeNetsplitBlock != nil) + precompileAddrs := vm.DefaultActivePrecompiles(rules) + return logger.NewAccessListTracer( + msg.AccessList(), + msg.From(), + *msg.To(), + precompileAddrs, + ) + case TracerJSON: + return logger.NewJSONLogger(logCfg, os.Stdout) + case TracerMarkdown: + return logger.NewMarkdownLogger(logCfg, os.Stdout) + case TracerStruct: + return logger.NewStructLogger(logCfg) + default: + return NewNoOpTracer() + } +} + +// TxTraceResult is the result of a single transaction trace during a block trace. +type TxTraceResult struct { + Result any `json:"result,omitempty"` // Trace results produced by the tracer + Error string `json:"error,omitempty"` // Trace failure produced by the tracer +} + +var _ vm.EVMLogger = &NoOpTracer{} + +// NoOpTracer is an empty implementation of vm.Tracer interface +type NoOpTracer struct{} + +// NewNoOpTracer creates a no-op vm.Tracer +func NewNoOpTracer() *NoOpTracer { + return &NoOpTracer{} +} + +// CaptureStart implements vm.Tracer interface +// +//nolint:revive // allow unused parameters to indicate expected signature +func (dt NoOpTracer) CaptureStart(env *vm.EVM, + from common.Address, + to common.Address, + create bool, + input []byte, + gas uint64, + value *big.Int) { +} + +// CaptureState implements vm.Tracer interface +// +//nolint:revive // allow unused parameters to indicate expected signature +func (dt NoOpTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +} + +// CaptureFault implements vm.Tracer interface +// +//nolint:revive // allow unused parameters to indicate expected signature +func (dt NoOpTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +} + +// CaptureEnd implements vm.Tracer interface +// +//nolint:revive // allow unused parameters to indicate expected signature +func (dt NoOpTracer) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {} + +// CaptureEnter implements vm.Tracer interface +// +//nolint:revive // allow unused parameters to indicate expected signature +func (dt NoOpTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +} + +// CaptureExit implements vm.Tracer interface +// +//nolint:revive // allow unused parameters to indicate expected signature +func (dt NoOpTracer) CaptureExit(output []byte, gasUsed uint64, err error) {} + +// CaptureTxStart implements vm.Tracer interface +// +//nolint:revive // allow unused parameters to indicate expected signature +func (dt NoOpTracer) CaptureTxStart(gasLimit uint64) {} + +// CaptureTxEnd implements vm.Tracer interface +// +//nolint:revive // allow unused parameters to indicate expected signature +func (dt NoOpTracer) CaptureTxEnd(restGas uint64) {} diff --git a/x/genmsg/genesis.go b/x/genmsg/genesis.go index b0a46de96..7e1a99415 100644 --- a/x/genmsg/genesis.go +++ b/x/genmsg/genesis.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - v1 "github.com/NibiruChain/nibiru/x/genmsg/v1" + v1 "github.com/NibiruChain/nibiru/v2/x/genmsg/v1" ) func anyToMsg(ir types.InterfaceRegistry, anyMsg *types.Any) (sdk.Msg, error) { diff --git a/x/genmsg/genesis_test.go b/x/genmsg/genesis_test.go index b4e601114..2fff010e3 100644 --- a/x/genmsg/genesis_test.go +++ b/x/genmsg/genesis_test.go @@ -11,7 +11,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/require" - v1 "github.com/NibiruChain/nibiru/x/genmsg/v1" + v1 "github.com/NibiruChain/nibiru/v2/x/genmsg/v1" ) type mockRouter struct { diff --git a/x/genmsg/integration_test.go b/x/genmsg/integration_test.go index 27f5c63c2..f85a87fbd 100644 --- a/x/genmsg/integration_test.go +++ b/x/genmsg/integration_test.go @@ -9,10 +9,10 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/genmsg" - v1 "github.com/NibiruChain/nibiru/x/genmsg/v1" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/genmsg" + v1 "github.com/NibiruChain/nibiru/v2/x/genmsg/v1" ) func TestIntegration(t *testing.T) { @@ -20,9 +20,9 @@ func TestIntegration(t *testing.T) { recvAddr := sdk.AccAddress("recv") encoding := app.MakeEncodingConfig() - appGenesis := app.NewDefaultGenesisState(encoding.Marshaler) + appGenesis := app.NewDefaultGenesisState(encoding.Codec) - appGenesis[banktypes.ModuleName] = encoding.Marshaler.MustMarshalJSON(&banktypes.GenesisState{ + appGenesis[banktypes.ModuleName] = encoding.Codec.MustMarshalJSON(&banktypes.GenesisState{ Balances: []banktypes.Balance{ { Address: senderAddr.String(), @@ -40,7 +40,7 @@ func TestIntegration(t *testing.T) { anyMsg, err := codectypes.NewAnyWithValue(testMsg) require.NoError(t, err) - appGenesis[genmsg.ModuleName] = encoding.Marshaler.MustMarshalJSON( + appGenesis[genmsg.ModuleName] = encoding.Codec.MustMarshalJSON( &v1.GenesisState{ Messages: []*codectypes.Any{anyMsg}, }, diff --git a/x/genmsg/module.go b/x/genmsg/module.go index f20ae9b74..98ff076c6 100644 --- a/x/genmsg/module.go +++ b/x/genmsg/module.go @@ -14,7 +14,7 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - v1 "github.com/NibiruChain/nibiru/x/genmsg/v1" + v1 "github.com/NibiruChain/nibiru/v2/x/genmsg/v1" ) const ( diff --git a/x/genmsg/v1/genmsg.pb.go b/x/genmsg/v1/genmsg.pb.go index 2a056f1aa..e6af4b761 100644 --- a/x/genmsg/v1/genmsg.pb.go +++ b/x/genmsg/v1/genmsg.pb.go @@ -75,7 +75,7 @@ func init() { func init() { proto.RegisterFile("nibiru/genmsg/v1/genmsg.proto", fileDescriptor_861b6a68d8c1c2d0) } var fileDescriptor_861b6a68d8c1c2d0 = []byte{ - // 188 bytes of a gzipped FileDescriptorProto + // 191 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcd, 0xcb, 0x4c, 0xca, 0x2c, 0x2a, 0xd5, 0x4f, 0x4f, 0xcd, 0xcb, 0x2d, 0x4e, 0xd7, 0x2f, 0x33, 0x84, 0xb2, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x04, 0x20, 0xd2, 0x7a, 0x50, 0xc1, 0x32, 0x43, 0x29, 0xc9, 0xf4, @@ -83,11 +83,11 @@ var fileDescriptor_861b6a68d8c1c2d0 = []byte{ 0xb1, 0x92, 0x03, 0x17, 0x8f, 0x7b, 0x6a, 0x5e, 0x6a, 0x71, 0x66, 0x71, 0x70, 0x49, 0x62, 0x49, 0xaa, 0x90, 0x01, 0x17, 0x47, 0x6e, 0x6a, 0x71, 0x71, 0x62, 0x7a, 0x6a, 0xb1, 0x04, 0xa3, 0x02, 0xb3, 0x06, 0xb7, 0x91, 0x88, 0x1e, 0x44, 0xb7, 0x1e, 0x4c, 0xb7, 0x9e, 0x63, 0x5e, 0x65, 0x10, - 0x5c, 0x95, 0x93, 0xf3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, - 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x69, 0xa6, + 0x5c, 0x95, 0x93, 0xdb, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, + 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xe9, 0xa4, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0xfb, 0x81, 0xdd, 0xe4, 0x9c, 0x91, - 0x98, 0x99, 0xa7, 0x0f, 0x75, 0x7e, 0x05, 0xc2, 0x03, 0x49, 0x6c, 0x60, 0xc3, 0x8d, 0x01, 0x01, - 0x00, 0x00, 0xff, 0xff, 0x90, 0xff, 0x3f, 0x5e, 0xdb, 0x00, 0x00, 0x00, + 0x98, 0x99, 0xa7, 0x0f, 0x75, 0x7e, 0x99, 0x91, 0x7e, 0x05, 0xc2, 0x0f, 0x49, 0x6c, 0x60, 0xf3, + 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x87, 0xf8, 0x6c, 0x64, 0xde, 0x00, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/x/inflation/client/cli/query.go b/x/inflation/client/cli/query.go index 53db6f67c..5da91da1e 100644 --- a/x/inflation/client/cli/query.go +++ b/x/inflation/client/cli/query.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // GetQueryCmd returns the cli query commands for the inflation module. diff --git a/x/inflation/client/cli/tx.go b/x/inflation/client/cli/tx.go index a66b46db0..e3c3b2554 100644 --- a/x/inflation/client/cli/tx.go +++ b/x/inflation/client/cli/tx.go @@ -3,13 +3,14 @@ package cli import ( "strings" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // GetTxCmd returns the transaction commands for this module @@ -99,19 +100,19 @@ $ nibid tx oracle edit-params --staking-proportion 0.6 --community-pool-proporti var stakingProportionDec sdk.Dec if stakingProportion, _ := cmd.Flags().GetString("staking-proportion"); stakingProportion != "" { - stakingProportionDec = sdk.MustNewDecFromStr(stakingProportion) + stakingProportionDec = math.LegacyMustNewDecFromStr(stakingProportion) msg.InflationDistribution.StakingRewards = stakingProportionDec } var communityPoolProportionDec sdk.Dec if communityPoolProportion, _ := cmd.Flags().GetString("community-pool-proportion"); communityPoolProportion != "" { - communityPoolProportionDec = sdk.MustNewDecFromStr(communityPoolProportion) + communityPoolProportionDec = math.LegacyMustNewDecFromStr(communityPoolProportion) msg.InflationDistribution.CommunityPool = communityPoolProportionDec } var strategicReservesProportionDec sdk.Dec if strategicReservesProportion, _ := cmd.Flags().GetString("strategic-reserves-proportion"); strategicReservesProportion != "" { - strategicReservesProportionDec = sdk.MustNewDecFromStr(strategicReservesProportion) + strategicReservesProportionDec = math.LegacyMustNewDecFromStr(strategicReservesProportion) msg.InflationDistribution.StrategicReserves = strategicReservesProportionDec } @@ -127,7 +128,7 @@ $ nibid tx oracle edit-params --staking-proportion 0.6 --community-pool-proporti polynomialFactorsArr := strings.Split(polynomialFactors, ",") realPolynomialFactors := make([]sdk.Dec, len(polynomialFactorsArr)) for i, factor := range polynomialFactorsArr { - factorDec := sdk.MustNewDecFromStr(factor) + factorDec := math.LegacyMustNewDecFromStr(factor) realPolynomialFactors[i] = factorDec } msg.PolynomialFactors = realPolynomialFactors diff --git a/x/inflation/genesis.go b/x/inflation/genesis.go index b55da1322..07c47b0ec 100644 --- a/x/inflation/genesis.go +++ b/x/inflation/genesis.go @@ -3,8 +3,8 @@ package inflation import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/inflation/keeper" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/keeper" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // InitGenesis import module genesis diff --git a/x/inflation/keeper/grpc_query.go b/x/inflation/keeper/grpc_query.go index 39cc78831..0d065e69f 100644 --- a/x/inflation/keeper/grpc_query.go +++ b/x/inflation/keeper/grpc_query.go @@ -5,8 +5,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/inflation/types" + "cosmossdk.io/math" + + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // querier implements the module's gRPC "QueryServer" interface @@ -80,7 +82,7 @@ func (k Keeper) CirculatingSupply( ctx := sdk.UnwrapSDKContext(c) circulatingSupply := k.GetCirculatingSupply(ctx, denoms.NIBI) - circulatingToDec := sdk.NewDecFromInt(circulatingSupply) + circulatingToDec := math.LegacyNewDecFromInt(circulatingSupply) coin := sdk.NewDecCoinFromDec(denoms.NIBI, circulatingToDec) return &types.QueryCirculatingSupplyResponse{CirculatingSupply: coin}, nil diff --git a/x/inflation/keeper/grpc_query_test.go b/x/inflation/keeper/grpc_query_test.go index 6c8e666b7..89bede6f9 100644 --- a/x/inflation/keeper/grpc_query_test.go +++ b/x/inflation/keeper/grpc_query_test.go @@ -6,12 +6,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/x/inflation/keeper" + "github.com/NibiruChain/nibiru/v2/x/inflation/keeper" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" - inflationtypes "github.com/NibiruChain/nibiru/x/inflation/types" + inflationtypes "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) type QueryServerSuite struct { diff --git a/x/inflation/keeper/hooks.go b/x/inflation/keeper/hooks.go index 0c78c3e0d..5c8dbbe36 100644 --- a/x/inflation/keeper/hooks.go +++ b/x/inflation/keeper/hooks.go @@ -7,14 +7,14 @@ import ( "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/denoms" - epochstypes "github.com/NibiruChain/nibiru/x/epochs/types" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + epochstypes "github.com/NibiruChain/nibiru/v2/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // Hooks implements module-specific calls ([epochstypes.EpochHooks]) that will -// occur at the end of every epoch. Hooks is meant for use with with -// `EpochsKeeper.SetHooks`. These functions run outside of the normal body of +// occur at the end of every epoch. Hooks is meant for use with +// `EpochsKeeper.SetHooks`. These functions run outside the normal body of // transactions. type Hooks struct { K Keeper diff --git a/x/inflation/keeper/hooks_test.go b/x/inflation/keeper/hooks_test.go index b54851ef9..6524ebdcd 100644 --- a/x/inflation/keeper/hooks_test.go +++ b/x/inflation/keeper/hooks_test.go @@ -5,16 +5,17 @@ import ( "testing" "time" + "cosmossdk.io/math" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - epochstypes "github.com/NibiruChain/nibiru/x/epochs/types" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + epochstypes "github.com/NibiruChain/nibiru/v2/x/epochs/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // TestEpochIdentifierAfterEpochEnd: Ensures that the amount in the community @@ -224,22 +225,22 @@ func TestManual(t *testing.T) { params.EpochsPerPeriod = 30 // y = 3 * x + 3 -> 3 nibi per epoch for period 0, 6 nibi per epoch for period 1 - params.PolynomialFactors = []sdk.Dec{sdk.NewDec(3), sdk.NewDec(3)} + params.PolynomialFactors = []sdk.Dec{math.LegacyNewDec(3), math.LegacyNewDec(3)} params.InflationDistribution = types.InflationDistribution{ - CommunityPool: sdk.ZeroDec(), - StakingRewards: sdk.OneDec(), - StrategicReserves: sdk.ZeroDec(), + CommunityPool: math.LegacyZeroDec(), + StakingRewards: math.LegacyOneDec(), + StrategicReserves: math.LegacyZeroDec(), } inflationKeeper.Params.Set(ctx, params) - require.Equal(t, sdk.ZeroInt(), GetBalanceStaking(ctx, nibiruApp)) + require.Equal(t, math.ZeroInt(), GetBalanceStaking(ctx, nibiruApp)) for i := 0; i < 42069; i++ { inflationKeeper.Hooks().AfterEpochEnd(ctx, epochstypes.DayEpochID, epochNumber) epochNumber++ } - require.Equal(t, sdk.ZeroInt(), GetBalanceStaking(ctx, nibiruApp)) + require.Equal(t, math.ZeroInt(), GetBalanceStaking(ctx, nibiruApp)) require.EqualValues(t, uint64(0), inflationKeeper.CurrentPeriod.Peek(ctx)) require.EqualValues(t, uint64(42069), inflationKeeper.NumSkippedEpochs.Peek(ctx)) @@ -259,10 +260,10 @@ func TestManual(t *testing.T) { // Period 0 - inflate 3M NIBI over 30 epochs or 100k uNIBI per epoch for i := 0; i < 30; i++ { inflationKeeper.Hooks().AfterEpochEnd(ctx, epochstypes.DayEpochID, epochNumber) - require.Equal(t, sdk.NewInt(100_000).Mul(sdk.NewInt(int64(i+1))), GetBalanceStaking(ctx, nibiruApp)) + require.Equal(t, math.NewInt(100_000).Mul(math.NewInt(int64(i+1))), GetBalanceStaking(ctx, nibiruApp)) epochNumber++ } - require.Equal(t, sdk.NewInt(3_000_000), GetBalanceStaking(ctx, nibiruApp)) + require.Equal(t, math.NewInt(3_000_000), GetBalanceStaking(ctx, nibiruApp)) require.EqualValues(t, uint64(1), inflationKeeper.CurrentPeriod.Peek(ctx)) require.EqualValues(t, uint64(42069), inflationKeeper.NumSkippedEpochs.Peek(ctx)) @@ -273,7 +274,7 @@ func TestManual(t *testing.T) { inflationKeeper.Hooks().AfterEpochEnd(ctx, epochstypes.DayEpochID, epochNumber) epochNumber++ } - require.Equal(t, sdk.NewInt(3_000_000), GetBalanceStaking(ctx, nibiruApp)) + require.Equal(t, math.NewInt(3_000_000), GetBalanceStaking(ctx, nibiruApp)) require.EqualValues(t, uint64(1), inflationKeeper.CurrentPeriod.Peek(ctx)) require.EqualValues(t, uint64(84138), inflationKeeper.NumSkippedEpochs.Peek(ctx)) @@ -283,10 +284,10 @@ func TestManual(t *testing.T) { // Period 1 - inflate 6M NIBI over 30 epochs or 200k uNIBI per epoch for i := 0; i < 30; i++ { inflationKeeper.Hooks().AfterEpochEnd(ctx, epochstypes.DayEpochID, epochNumber) - require.Equal(t, sdk.NewInt(3_000_000).Add(sdk.NewInt(200_000).Mul(sdk.NewInt(int64(i+1)))), GetBalanceStaking(ctx, nibiruApp)) + require.Equal(t, math.NewInt(3_000_000).Add(math.NewInt(200_000).Mul(math.NewInt(int64(i+1)))), GetBalanceStaking(ctx, nibiruApp)) epochNumber++ } - require.Equal(t, sdk.NewInt(9_000_000), GetBalanceStaking(ctx, nibiruApp)) + require.Equal(t, math.NewInt(9_000_000), GetBalanceStaking(ctx, nibiruApp)) require.EqualValues(t, uint64(2), inflationKeeper.CurrentPeriod.Peek(ctx)) require.EqualValues(t, uint64(84138), inflationKeeper.NumSkippedEpochs.Peek(ctx)) diff --git a/x/inflation/keeper/inflation.go b/x/inflation/keeper/inflation.go index 16eabb81a..254262932 100644 --- a/x/inflation/keeper/inflation.go +++ b/x/inflation/keeper/inflation.go @@ -3,11 +3,12 @@ package keeper import ( "fmt" + "cosmossdk.io/math" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // MintAndAllocateInflation mints and allocates tokens based on the polynomial @@ -54,7 +55,7 @@ func (k Keeper) MintCoins(ctx sdk.Context, coin sdk.Coin) error { } // AllocatePolynomialInflation allocates coins from the inflation to external -// modules according to proportions proportions: +// modules according to proportions: // // Returns: // - staking: Tokens minted for staking inflation that go to the decentralized @@ -131,7 +132,7 @@ func (k Keeper) GetProportions( ) sdk.Coin { return sdk.Coin{ Denom: coin.Denom, - Amount: sdk.NewDecFromInt(coin.Amount).Mul(proportion).TruncateInt(), + Amount: math.LegacyNewDecFromInt(coin.Amount).Mul(proportion).TruncateInt(), } } @@ -145,21 +146,21 @@ func (k Keeper) GetCirculatingSupply(ctx sdk.Context, mintDenom string) sdkmath. func (k Keeper) GetInflationRate(ctx sdk.Context, mintDenom string) sdk.Dec { epochMintProvision := k.GetEpochMintProvision(ctx) if epochMintProvision.IsZero() { - return sdk.ZeroDec() + return math.LegacyZeroDec() } circulatingSupply := k.GetCirculatingSupply(ctx, mintDenom) if circulatingSupply.IsZero() { - return sdk.ZeroDec() + return math.LegacyZeroDec() } // EpochMintProvision * 365 / circulatingSupply * 100 - circulatingSupplyToDec := sdk.NewDecFromInt(circulatingSupply) + circulatingSupplyToDec := math.LegacyNewDecFromInt(circulatingSupply) return epochMintProvision. MulInt64(int64(k.GetEpochsPerPeriod(ctx))). MulInt64(int64(k.GetPeriodsPerYear(ctx))). Quo(circulatingSupplyToDec). - Mul(sdk.NewDec(100)) + Mul(math.LegacyNewDec(100)) } // GetEpochMintProvision retrieves necessary params KV storage diff --git a/x/inflation/keeper/inflation_test.go b/x/inflation/keeper/inflation_test.go index f652ac352..6831f656f 100644 --- a/x/inflation/keeper/inflation_test.go +++ b/x/inflation/keeper/inflation_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,11 +13,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/inflation/types" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) func init() { @@ -36,35 +38,35 @@ func TestMintAndAllocateInflation(t *testing.T) { }{ { name: "pass", - coinsToMint: sdk.NewCoin(denoms.NIBI, sdk.NewInt(1_000_000)), - expectedStakingAmt: sdk.NewCoin(denoms.NIBI, sdk.NewInt(281_250)), - expectedStrategicAmt: sdk.NewCoin(denoms.NIBI, sdk.NewInt(363_925)), - expectedCommunityAmt: sdk.NewCoin(denoms.NIBI, sdk.NewInt(354_825)), - expectedStakingRewardsBalance: sdk.NewCoin(denoms.NIBI, sdk.NewInt(281_250)), - expectedStrategicReservesBalance: sdk.NewCoin(denoms.NIBI, sdk.NewInt(363_925)), - expectedCommunityPoolBalance: sdk.NewDecCoins(sdk.NewDecCoin(denoms.NIBI, sdk.NewInt(354_825))), + coinsToMint: sdk.NewCoin(denoms.NIBI, math.NewInt(1_000_000)), + expectedStakingAmt: sdk.NewCoin(denoms.NIBI, math.NewInt(281_250)), + expectedStrategicAmt: sdk.NewCoin(denoms.NIBI, math.NewInt(363_925)), + expectedCommunityAmt: sdk.NewCoin(denoms.NIBI, math.NewInt(354_825)), + expectedStakingRewardsBalance: sdk.NewCoin(denoms.NIBI, math.NewInt(281_250)), + expectedStrategicReservesBalance: sdk.NewCoin(denoms.NIBI, math.NewInt(363_925)), + expectedCommunityPoolBalance: sdk.NewDecCoins(sdk.NewDecCoin(denoms.NIBI, math.NewInt(354_825))), rootAccount: "nibi1qyqf35fkhn73hjr70442fctpq8prpqr9ysj9sn", }, { name: "pass - no coins minted ", - coinsToMint: sdk.NewCoin(denoms.NIBI, sdk.ZeroInt()), + coinsToMint: sdk.NewCoin(denoms.NIBI, math.ZeroInt()), expectedStakingAmt: sdk.Coin{}, expectedStrategicAmt: sdk.Coin{}, expectedCommunityAmt: sdk.Coin{}, - expectedStakingRewardsBalance: sdk.NewCoin(denoms.NIBI, sdk.ZeroInt()), - expectedStrategicReservesBalance: sdk.NewCoin(denoms.NIBI, sdk.ZeroInt()), + expectedStakingRewardsBalance: sdk.NewCoin(denoms.NIBI, math.ZeroInt()), + expectedStrategicReservesBalance: sdk.NewCoin(denoms.NIBI, math.ZeroInt()), expectedCommunityPoolBalance: nil, rootAccount: "nibi1qyqf35fkhn73hjr70442fctpq8prpqr9ysj9sn", }, { name: "pass - no root account", - coinsToMint: sdk.NewCoin(denoms.NIBI, sdk.NewInt(1_000_000)), - expectedStakingAmt: sdk.NewCoin(denoms.NIBI, sdk.NewInt(281_250)), - expectedStrategicAmt: sdk.NewCoin(denoms.NIBI, sdk.NewInt(363_925)), - expectedCommunityAmt: sdk.NewCoin(denoms.NIBI, sdk.NewInt(354_825)), - expectedStakingRewardsBalance: sdk.NewCoin(denoms.NIBI, sdk.NewInt(281_250)), - expectedStrategicReservesBalance: sdk.NewCoin(denoms.NIBI, sdk.NewInt(363_925)), - expectedCommunityPoolBalance: sdk.NewDecCoins(sdk.NewDecCoin(denoms.NIBI, sdk.NewInt(354_825))), + coinsToMint: sdk.NewCoin(denoms.NIBI, math.NewInt(1_000_000)), + expectedStakingAmt: sdk.NewCoin(denoms.NIBI, math.NewInt(281_250)), + expectedStrategicAmt: sdk.NewCoin(denoms.NIBI, math.NewInt(363_925)), + expectedCommunityAmt: sdk.NewCoin(denoms.NIBI, math.NewInt(354_825)), + expectedStakingRewardsBalance: sdk.NewCoin(denoms.NIBI, math.NewInt(281_250)), + expectedStrategicReservesBalance: sdk.NewCoin(denoms.NIBI, math.NewInt(363_925)), + expectedCommunityPoolBalance: sdk.NewDecCoins(sdk.NewDecCoin(denoms.NIBI, math.NewInt(354_825))), rootAccount: "", }, } @@ -135,7 +137,7 @@ func TestGetCirculatingSupplyAndInflationRate(t *testing.T) { }{ { "no epochs per period", - sdk.TokensFromConsensusPower(400_000_000, sdk.DefaultPowerReduction), + sdk.TokensFromConsensusPower(400_000_000-100_000_001, sdk.DefaultPowerReduction), func(nibiruApp *app.NibiruApp, ctx sdk.Context) { nibiruApp.InflationKeeper.Params.Set(ctx, types.Params{ EpochsPerPeriod: 0, @@ -144,27 +146,27 @@ func TestGetCirculatingSupplyAndInflationRate(t *testing.T) { InflationDistribution: types.DefaultInflationDistribution, }) }, - sdk.ZeroDec(), + math.LegacyZeroDec(), }, { "high supply", - sdk.TokensFromConsensusPower(800_000_000, sdk.DefaultPowerReduction), + sdk.TokensFromConsensusPower(800_000_000-100_000_001, sdk.DefaultPowerReduction), func(nibiruApp *app.NibiruApp, ctx sdk.Context) { params := nibiruApp.InflationKeeper.GetParams(ctx) params.InflationEnabled = true nibiruApp.InflationKeeper.Params.Set(ctx, params) }, - sdk.MustNewDecFromStr("26.741197359810099000"), + math.LegacyMustNewDecFromStr("26.741197359810099000"), }, { "low supply", - sdk.TokensFromConsensusPower(400_000_000, sdk.DefaultPowerReduction), + sdk.TokensFromConsensusPower(400_000_000-100_000_001, sdk.DefaultPowerReduction), func(nibiruApp *app.NibiruApp, ctx sdk.Context) { params := nibiruApp.InflationKeeper.GetParams(ctx) params.InflationEnabled = true nibiruApp.InflationKeeper.Params.Set(ctx, params) }, - sdk.MustNewDecFromStr("53.482394719620198000"), + math.LegacyMustNewDecFromStr("53.482394719620198000"), }, } for _, tc := range testCases { @@ -182,7 +184,7 @@ func TestGetCirculatingSupplyAndInflationRate(t *testing.T) { require.NoError(t, err) circulatingSupply := nibiruApp.InflationKeeper.GetCirculatingSupply(ctx, denoms.NIBI) - require.EqualValues(t, tc.supply, circulatingSupply) + require.EqualValues(t, tc.supply.Add(sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction)), circulatingSupply) inflationRate := nibiruApp.InflationKeeper.GetInflationRate(ctx, denoms.NIBI) require.Equal(t, tc.expInflationRate, inflationRate) diff --git a/x/inflation/keeper/keeper.go b/x/inflation/keeper/keeper.go index 03327ac46..893dcedf2 100644 --- a/x/inflation/keeper/keeper.go +++ b/x/inflation/keeper/keeper.go @@ -8,12 +8,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // Keeper of the inflation module. Keepers are module-specific "gate keepers" // responsible for encapsulating access to the key-value stores (state) of the -// network. The functions on the Keeper contain all of the business logic for +// network. The functions on the Keeper contain all the business logic for // reading and modifying state. type Keeper struct { cdc codec.BinaryCodec @@ -27,7 +27,7 @@ type Keeper struct { distrKeeper types.DistrKeeper stakingKeeper types.StakingKeeper sudoKeeper types.SudoKeeper - // feeCollectorName is the name of of x/auth module's fee collector module + // feeCollectorName is the name of x/auth module's fee collector module // account, "fee_collector", which collects transaction fees for distribution // to all stakers. // By sending staking inflation to the fee collector, the tokens are properly @@ -89,3 +89,13 @@ func NewKeeper( func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/"+types.ModuleName) } + +func (k Keeper) Burn(ctx sdk.Context, coins sdk.Coins, sender sdk.AccAddress) error { + if err := k.bankKeeper.SendCoinsFromAccountToModule( + ctx, sender, types.ModuleName, coins, + ); err != nil { + return err + } + + return k.bankKeeper.BurnCoins(ctx, types.ModuleName, coins) +} diff --git a/x/inflation/keeper/keeper_test.go b/x/inflation/keeper/keeper_test.go new file mode 100644 index 000000000..b3f24b2ee --- /dev/null +++ b/x/inflation/keeper/keeper_test.go @@ -0,0 +1,72 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" +) + +func init() { + testapp.EnsureNibiruPrefix() +} + +func TestBurn(t *testing.T) { + testCases := []struct { + name string + sender sdk.AccAddress + mintCoin sdk.Coin + burnCoin sdk.Coin + expectedErr error + }{ + { + name: "pass", + sender: testutil.AccAddress(), + mintCoin: sdk.NewCoin("unibi", math.NewInt(100)), + burnCoin: sdk.NewCoin("unibi", math.NewInt(100)), + expectedErr: nil, + }, + { + name: "not enough coins", + sender: testutil.AccAddress(), + mintCoin: sdk.NewCoin("unibi", math.NewInt(100)), + burnCoin: sdk.NewCoin("unibi", math.NewInt(101)), + expectedErr: fmt.Errorf("spendable balance 100unibi is smaller than 101unibi: insufficient funds"), + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) { + nibiruApp, ctx := testapp.NewNibiruTestAppAndContext() + + // mint and send money to the sender + require.NoError(t, + nibiruApp.BankKeeper.MintCoins( + ctx, types.ModuleName, sdk.NewCoins(tc.mintCoin))) + require.NoError(t, + nibiruApp.BankKeeper.SendCoinsFromModuleToAccount( + ctx, types.ModuleName, tc.sender, sdk.NewCoins(tc.mintCoin)), + ) + + supply := nibiruApp.BankKeeper.GetSupply(ctx, "unibi") + require.Equal(t, tc.mintCoin.Amount.Add(sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction)), supply.Amount) + + // Burn coins + err := nibiruApp.InflationKeeper.Burn(ctx, sdk.NewCoins(tc.burnCoin), tc.sender) + supply = nibiruApp.BankKeeper.GetSupply(ctx, "unibi") + if tc.expectedErr != nil { + require.EqualError(t, err, tc.expectedErr.Error()) + require.Equal(t, tc.mintCoin.Amount.Add(sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction)), supply.Amount) + } else { + require.NoError(t, err) + require.Equal(t, sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction), supply.Amount) + } + }) + } +} diff --git a/x/inflation/keeper/msg_server.go b/x/inflation/keeper/msg_server.go index b1cc8048a..431006782 100644 --- a/x/inflation/keeper/msg_server.go +++ b/x/inflation/keeper/msg_server.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) type msgServer struct { diff --git a/x/inflation/keeper/msg_server_test.go b/x/inflation/keeper/msg_server_test.go index 2f1e1483f..41c1bcefd 100644 --- a/x/inflation/keeper/msg_server_test.go +++ b/x/inflation/keeper/msg_server_test.go @@ -3,13 +3,13 @@ package keeper_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/inflation/keeper" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/inflation/keeper" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) func TestMsgToggleInflation(t *testing.T) { @@ -48,7 +48,7 @@ func TestMsgEditInflationParams(t *testing.T) { params := app.InflationKeeper.GetParams(ctx) require.NotEqualValues(t, params.EpochsPerPeriod, 42) - newEpochPerPeriod := sdk.NewInt(42) + newEpochPerPeriod := math.NewInt(42) msg := types.MsgEditInflationParams{ Sender: testutil.AccAddress().String(), EpochsPerPeriod: &newEpochPerPeriod, diff --git a/x/inflation/keeper/params.go b/x/inflation/keeper/params.go index 3068045de..4df4ff3b4 100644 --- a/x/inflation/keeper/params.go +++ b/x/inflation/keeper/params.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) func (k Keeper) GetParams(ctx sdk.Context) types.Params { diff --git a/x/inflation/keeper/sudo.go b/x/inflation/keeper/sudo.go index aa01e10a8..f1efae0d0 100644 --- a/x/inflation/keeper/sudo.go +++ b/x/inflation/keeper/sudo.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - inflationtypes "github.com/NibiruChain/nibiru/x/inflation/types" + inflationtypes "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // Sudo extends the Keeper with sudo functions. See [x/sudo]. diff --git a/x/inflation/keeper/sudo_test.go b/x/inflation/keeper/sudo_test.go index c527c2a7e..322464c70 100644 --- a/x/inflation/keeper/sudo_test.go +++ b/x/inflation/keeper/sudo_test.go @@ -3,12 +3,13 @@ package keeper_test import ( "testing" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - inflationKeeper "github.com/NibiruChain/nibiru/x/inflation/keeper" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + inflationKeeper "github.com/NibiruChain/nibiru/v2/x/inflation/keeper" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) func TestSuiteInflationSudo(t *testing.T) { @@ -22,7 +23,7 @@ type SuiteInflationSudo struct { func (s *SuiteInflationSudo) TestMergeInflationParams() { currentParams := types.DefaultParams() - newEpochsPerPeriod := sdk.NewInt(4) + newEpochsPerPeriod := math.NewInt(4) paramsChanges := types.MsgEditInflationParams{ EpochsPerPeriod: &newEpochsPerPeriod, } @@ -40,9 +41,9 @@ func (s *SuiteInflationSudo) TestMergeInflationParams() { // Test a change to all parameters newInflationDistribution := types.InflationDistribution{ - CommunityPool: sdk.MustNewDecFromStr("0.8"), - StakingRewards: sdk.MustNewDecFromStr("0.1"), - StrategicReserves: sdk.MustNewDecFromStr("0.1"), + CommunityPool: math.LegacyMustNewDecFromStr("0.8"), + StakingRewards: math.LegacyMustNewDecFromStr("0.1"), + StrategicReserves: math.LegacyMustNewDecFromStr("0.1"), } paramsChanges = types.MsgEditInflationParams{ @@ -50,8 +51,8 @@ func (s *SuiteInflationSudo) TestMergeInflationParams() { PeriodsPerYear: &newEpochsPerPeriod, MaxPeriod: &newEpochsPerPeriod, PolynomialFactors: []sdk.Dec{ - sdk.MustNewDecFromStr("0.1"), - sdk.MustNewDecFromStr("0.2"), + math.LegacyMustNewDecFromStr("0.1"), + math.LegacyMustNewDecFromStr("0.2"), }, InflationDistribution: &newInflationDistribution, } @@ -62,8 +63,8 @@ func (s *SuiteInflationSudo) TestMergeInflationParams() { s.Require().EqualValues(4, paramsAfter.PeriodsPerYear) s.Require().EqualValues(4, paramsAfter.MaxPeriod) s.Require().EqualValues([]sdk.Dec{ - sdk.MustNewDecFromStr("0.1"), - sdk.MustNewDecFromStr("0.2"), + math.LegacyMustNewDecFromStr("0.1"), + math.LegacyMustNewDecFromStr("0.2"), }, paramsAfter.PolynomialFactors) s.Require().EqualValues(newInflationDistribution, paramsAfter.InflationDistribution) } @@ -72,17 +73,17 @@ func (s *SuiteInflationSudo) TestEditInflationParams() { nibiru, ctx := testapp.NewNibiruTestAppAndContext() // Change to all non-defaults to test EditInflationParams as a setter . - epochsPerPeriod := sdk.NewInt(1_234) - periodsPerYear := sdk.NewInt(1_234) - maxPeriod := sdk.NewInt(1_234) + epochsPerPeriod := math.NewInt(1_234) + periodsPerYear := math.NewInt(1_234) + maxPeriod := math.NewInt(1_234) polynomialFactors := []sdk.Dec{ - sdk.MustNewDecFromStr("0.1"), - sdk.MustNewDecFromStr("0.2"), + math.LegacyMustNewDecFromStr("0.1"), + math.LegacyMustNewDecFromStr("0.2"), } inflationDistribution := types.InflationDistribution{ - CommunityPool: sdk.MustNewDecFromStr("0.8"), - StakingRewards: sdk.MustNewDecFromStr("0.1"), - StrategicReserves: sdk.MustNewDecFromStr("0.1"), + CommunityPool: math.LegacyMustNewDecFromStr("0.8"), + StakingRewards: math.LegacyMustNewDecFromStr("0.1"), + StrategicReserves: math.LegacyMustNewDecFromStr("0.1"), } msgEditParams := types.MsgEditInflationParams{ EpochsPerPeriod: &epochsPerPeriod, diff --git a/x/inflation/module.go b/x/inflation/module.go index 3eac91cfa..35d352380 100644 --- a/x/inflation/module.go +++ b/x/inflation/module.go @@ -17,9 +17,10 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/inflation/client/cli" - "github.com/NibiruChain/nibiru/x/inflation/keeper" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/client/cli" + "github.com/NibiruChain/nibiru/v2/x/inflation/keeper" + "github.com/NibiruChain/nibiru/v2/x/inflation/simulation" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // type check to ensure the interface is properly implemented @@ -154,7 +155,8 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw // AppModuleSimulation functions // GenerateGenesisState creates a randomized GenState of the inflation module. -func (am AppModule) GenerateGenesisState(_ *module.SimulationState) { +func (am AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) } // ProposalContents doesn't return any content functions for governance proposals. diff --git a/x/inflation/simulation/genesis.go b/x/inflation/simulation/genesis.go index 25add08db..edc5d7b0e 100644 --- a/x/inflation/simulation/genesis.go +++ b/x/inflation/simulation/genesis.go @@ -6,10 +6,11 @@ import ( "encoding/json" "fmt" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/NibiruChain/nibiru/x/inflation/types" + "github.com/NibiruChain/nibiru/v2/x/inflation/types" ) // RandomizedGenState generates a random GenesisState for distribution @@ -18,17 +19,17 @@ func RandomizedGenState(simState *module.SimulationState) { Params: types.Params{ InflationEnabled: true, PolynomialFactors: []sdk.Dec{ - sdk.MustNewDecFromStr("-0.00014851"), - sdk.MustNewDecFromStr("0.07501029"), - sdk.MustNewDecFromStr("-19.04983993"), - sdk.MustNewDecFromStr("3158.89198346"), - sdk.MustNewDecFromStr("-338072.17402939"), - sdk.MustNewDecFromStr("17999834.20786474"), + math.LegacyMustNewDecFromStr("-0.00014851"), + math.LegacyMustNewDecFromStr("0.07501029"), + math.LegacyMustNewDecFromStr("-19.04983993"), + math.LegacyMustNewDecFromStr("3158.89198346"), + math.LegacyMustNewDecFromStr("-338072.17402939"), + math.LegacyMustNewDecFromStr("17999834.20786474"), }, InflationDistribution: types.InflationDistribution{ - CommunityPool: sdk.NewDecWithPrec(35_142714, 8), // 35.142714% - StakingRewards: sdk.NewDecWithPrec(27_855672, 8), // 27.855672% - StrategicReserves: sdk.NewDecWithPrec(37_001614, 8), // 37.001614% + CommunityPool: math.LegacyNewDecWithPrec(35_142714, 8), // 35.142714% + StakingRewards: math.LegacyNewDecWithPrec(27_855672, 8), // 27.855672% + StrategicReserves: math.LegacyNewDecWithPrec(37_001614, 8), // 37.001614% }, EpochsPerPeriod: 30, PeriodsPerYear: 12, diff --git a/x/inflation/types/event.pb.go b/x/inflation/types/event.pb.go index c5fd42816..64335efad 100644 --- a/x/inflation/types/event.pb.go +++ b/x/inflation/types/event.pb.go @@ -93,28 +93,28 @@ func init() { func init() { proto.RegisterFile("nibiru/inflation/v1/event.proto", fileDescriptor_18fa0385facaf5d9) } var fileDescriptor_18fa0385facaf5d9 = []byte{ - // 332 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0xbd, 0x4e, 0xf3, 0x30, - 0x14, 0x86, 0x93, 0x7e, 0xd2, 0x37, 0x04, 0x51, 0x20, 0xfc, 0x28, 0x54, 0xc2, 0xad, 0x32, 0x31, - 0xd9, 0x0a, 0x6c, 0x8c, 0x2d, 0x0c, 0x5d, 0x10, 0xca, 0xc8, 0x12, 0xd9, 0xc1, 0xa4, 0x16, 0x89, - 0x4f, 0x65, 0x3b, 0x81, 0xde, 0x05, 0x97, 0x55, 0x89, 0xa5, 0x23, 0x53, 0x85, 0xda, 0x3b, 0xe0, - 0x0a, 0x50, 0xea, 0xb4, 0x82, 0x2e, 0xdd, 0x8e, 0xde, 0x73, 0xce, 0xf3, 0x0c, 0xaf, 0xd7, 0x95, - 0x82, 0x09, 0x55, 0x12, 0x21, 0x9f, 0x73, 0x6a, 0x04, 0x48, 0x52, 0x45, 0x84, 0x57, 0x5c, 0x1a, - 0x3c, 0x56, 0x60, 0xc0, 0x3f, 0xb6, 0x07, 0x78, 0x73, 0x80, 0xab, 0xa8, 0x73, 0x92, 0x41, 0x06, - 0xab, 0x3d, 0xa9, 0x27, 0x7b, 0xda, 0x41, 0x29, 0xe8, 0x02, 0x34, 0x61, 0x54, 0x73, 0x52, 0x45, - 0x8c, 0x1b, 0x1a, 0x91, 0x14, 0x84, 0xb4, 0xfb, 0xf0, 0xa3, 0xe5, 0x75, 0xee, 0x6a, 0xf4, 0x70, - 0xcd, 0xba, 0x15, 0xda, 0x28, 0xc1, 0xca, 0x7a, 0xf6, 0x99, 0x77, 0xa0, 0x0d, 0x7d, 0x11, 0x32, - 0x4b, 0x14, 0x7f, 0xa5, 0xea, 0x49, 0x07, 0x6e, 0xcf, 0xbd, 0xdc, 0xbb, 0x3a, 0xc7, 0x16, 0x8c, - 0x6b, 0x30, 0x6e, 0xc0, 0x78, 0x00, 0x42, 0xf6, 0xd1, 0x74, 0xde, 0x75, 0xbe, 0xe7, 0xdd, 0xb3, - 0x09, 0x2d, 0xf2, 0x9b, 0x70, 0xeb, 0x3f, 0x8c, 0xdb, 0x4d, 0x12, 0xdb, 0xc0, 0x1f, 0x79, 0x47, - 0xda, 0x28, 0x6a, 0x78, 0x26, 0xd2, 0x44, 0x71, 0xcd, 0x55, 0xc5, 0x83, 0xd6, 0x2e, 0x4b, 0xaf, - 0xb1, 0x04, 0x6b, 0xcb, 0x16, 0x21, 0x8c, 0x0f, 0x37, 0x59, 0x6c, 0x23, 0x3f, 0xf1, 0xda, 0x29, - 0x14, 0x45, 0x29, 0x85, 0x99, 0x24, 0x63, 0x80, 0x3c, 0xf8, 0xb7, 0x4b, 0x73, 0xd1, 0x68, 0x4e, - 0xad, 0xe6, 0xef, 0x7b, 0x18, 0xef, 0x6f, 0x82, 0x07, 0x80, 0xbc, 0x3f, 0x9c, 0x2e, 0x90, 0x3b, - 0x5b, 0x20, 0xf7, 0x6b, 0x81, 0xdc, 0xf7, 0x25, 0x72, 0x66, 0x4b, 0xe4, 0x7c, 0x2e, 0x91, 0xf3, - 0x48, 0x32, 0x61, 0x46, 0x25, 0xc3, 0x29, 0x14, 0xe4, 0x7e, 0xd5, 0xde, 0x60, 0x44, 0x85, 0x24, - 0x4d, 0xd5, 0x6f, 0xbf, 0xca, 0x36, 0x93, 0x31, 0xd7, 0xec, 0xff, 0xaa, 0x9f, 0xeb, 0x9f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x2c, 0x97, 0xd4, 0x83, 0x0d, 0x02, 0x00, 0x00, + // 336 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0xbf, 0x4e, 0xeb, 0x30, + 0x14, 0xc6, 0x93, 0x5e, 0xe9, 0x0e, 0xb9, 0xba, 0x05, 0xc2, 0x1f, 0x85, 0x4a, 0xb8, 0x55, 0x26, + 0x26, 0x5b, 0x29, 0x1b, 0x63, 0x0b, 0x03, 0x12, 0x42, 0x28, 0x23, 0x4b, 0x64, 0x07, 0x93, 0x5a, + 0x24, 0x3e, 0x95, 0xed, 0x04, 0xfa, 0x16, 0x3c, 0x56, 0x25, 0x96, 0x8e, 0x4c, 0x15, 0x6a, 0xdf, + 0x80, 0x27, 0x40, 0xa9, 0xd3, 0x0a, 0xba, 0x74, 0x3b, 0xfa, 0xce, 0x39, 0xbf, 0xdf, 0xf0, 0x79, + 0x5d, 0x29, 0x98, 0x50, 0x25, 0x11, 0xf2, 0x29, 0xa7, 0x46, 0x80, 0x24, 0x55, 0x44, 0x78, 0xc5, + 0xa5, 0xc1, 0x63, 0x05, 0x06, 0xfc, 0x43, 0x7b, 0x80, 0x37, 0x07, 0xb8, 0x8a, 0x3a, 0x47, 0x19, + 0x64, 0xb0, 0xda, 0x93, 0x7a, 0xb2, 0xa7, 0x1d, 0x94, 0x82, 0x2e, 0x40, 0x13, 0x46, 0x35, 0x27, + 0x55, 0xc4, 0xb8, 0xa1, 0x11, 0x49, 0x41, 0x48, 0xbb, 0x0f, 0xdf, 0x5b, 0x5e, 0xe7, 0xba, 0x46, + 0xdf, 0xac, 0x59, 0x57, 0x42, 0x1b, 0x25, 0x58, 0x59, 0xcf, 0x3e, 0xf3, 0xf6, 0xb4, 0xa1, 0xcf, + 0x42, 0x66, 0x89, 0xe2, 0x2f, 0x54, 0x3d, 0xea, 0xc0, 0xed, 0xb9, 0xe7, 0xff, 0xfa, 0xa7, 0xd8, + 0x82, 0x71, 0x0d, 0xc6, 0x0d, 0x18, 0x0f, 0x41, 0xc8, 0x01, 0x9a, 0xce, 0xbb, 0xce, 0xd7, 0xbc, + 0x7b, 0x32, 0xa1, 0x45, 0x7e, 0x19, 0x6e, 0xfd, 0x87, 0x71, 0xbb, 0x49, 0x62, 0x1b, 0xf8, 0x23, + 0xef, 0x40, 0x1b, 0x45, 0x0d, 0xcf, 0x44, 0x9a, 0x28, 0xae, 0xb9, 0xaa, 0x78, 0xd0, 0xda, 0x65, + 0xe9, 0x35, 0x96, 0x60, 0x6d, 0xd9, 0x22, 0x84, 0xf1, 0xfe, 0x26, 0x8b, 0x6d, 0xe4, 0x27, 0x5e, + 0x3b, 0x85, 0xa2, 0x28, 0xa5, 0x30, 0x93, 0x64, 0x0c, 0x90, 0x07, 0x7f, 0x76, 0x69, 0xce, 0x1a, + 0xcd, 0xb1, 0xd5, 0xfc, 0x7e, 0x0f, 0xe3, 0xff, 0x9b, 0xe0, 0x1e, 0x20, 0x1f, 0xdc, 0x4e, 0x17, + 0xc8, 0x9d, 0x2d, 0x90, 0xfb, 0xb9, 0x40, 0xee, 0xdb, 0x12, 0x39, 0xb3, 0x25, 0x72, 0x3e, 0x96, + 0xc8, 0x79, 0xe8, 0x67, 0xc2, 0x8c, 0x4a, 0x86, 0x53, 0x28, 0xc8, 0xdd, 0xaa, 0xbd, 0xe1, 0x88, + 0x0a, 0x49, 0x9a, 0xaa, 0xab, 0x3e, 0x79, 0xfd, 0xd1, 0xb7, 0x99, 0x8c, 0xb9, 0x66, 0x7f, 0x57, + 0x15, 0x5d, 0x7c, 0x07, 0x00, 0x00, 0xff, 0xff, 0x9b, 0x1d, 0x75, 0xa0, 0x10, 0x02, 0x00, 0x00, } func (m *EventInflationDistribution) Marshal() (dAtA []byte, err error) { diff --git a/x/inflation/types/genesis.pb.go b/x/inflation/types/genesis.pb.go index 522028269..6a4473a0f 100644 --- a/x/inflation/types/genesis.pb.go +++ b/x/inflation/types/genesis.pb.go @@ -196,36 +196,36 @@ func init() { func init() { proto.RegisterFile("nibiru/inflation/v1/genesis.proto", fileDescriptor_2d00e2bb98c08f74) } var fileDescriptor_2d00e2bb98c08f74 = []byte{ - // 461 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x52, 0xc1, 0x6e, 0x13, 0x31, - 0x10, 0xcd, 0x36, 0x61, 0xa1, 0x2e, 0x94, 0xc6, 0xa5, 0xd5, 0xaa, 0x88, 0x6d, 0x28, 0x02, 0x45, - 0x45, 0xac, 0xd5, 0x70, 0xe2, 0x1a, 0x5a, 0x50, 0x2f, 0x28, 0x4a, 0x4f, 0x20, 0xa1, 0x95, 0x77, - 0x77, 0xba, 0x6b, 0x35, 0xbb, 0xb6, 0x6c, 0xa7, 0x4a, 0xfe, 0x80, 0x23, 0x5f, 0xc1, 0xb7, 0xf4, - 0xd8, 0x23, 0xe2, 0x50, 0xa1, 0xe4, 0x47, 0xaa, 0xd8, 0x6e, 0x92, 0xc3, 0x9e, 0xec, 0x99, 0xf7, - 0xe6, 0xcd, 0xcc, 0xd3, 0xa0, 0xd7, 0x15, 0x4b, 0x98, 0x1c, 0x13, 0x56, 0x5d, 0x8e, 0xa8, 0x66, - 0xbc, 0x22, 0xd7, 0x27, 0x24, 0x87, 0x0a, 0x14, 0x53, 0x91, 0x90, 0x5c, 0x73, 0xbc, 0x6b, 0x29, - 0xd1, 0x92, 0x12, 0x5d, 0x9f, 0x1c, 0xbc, 0xc8, 0x79, 0xce, 0x0d, 0x4e, 0x16, 0x3f, 0x4b, 0x3d, - 0x78, 0x53, 0xa7, 0xb6, 0xaa, 0x33, 0xa4, 0xa3, 0x5f, 0x1e, 0x7a, 0xfa, 0xd5, 0x76, 0xb8, 0xd0, - 0x54, 0x03, 0xfe, 0x84, 0x7c, 0x41, 0x25, 0x2d, 0x55, 0xe0, 0x75, 0xbc, 0xee, 0x56, 0xef, 0x65, - 0x54, 0xd3, 0x31, 0x1a, 0x18, 0x4a, 0xbf, 0x75, 0x73, 0x77, 0xd8, 0x18, 0xba, 0x02, 0xbc, 0x8f, - 0x7c, 0x01, 0x92, 0xf1, 0x2c, 0xd8, 0xe8, 0x78, 0xdd, 0xd6, 0xd0, 0x45, 0xf8, 0x2d, 0xda, 0x56, - 0x57, 0x4c, 0x08, 0xc8, 0x62, 0x10, 0x3c, 0x2d, 0x54, 0xd0, 0x34, 0xf8, 0x33, 0x97, 0x3d, 0x33, - 0xc9, 0xa3, 0x3f, 0x4d, 0xe4, 0x5b, 0x5d, 0xfc, 0x1e, 0xb5, 0x97, 0xed, 0x62, 0xa8, 0x68, 0x32, - 0x82, 0xcc, 0xcc, 0xf3, 0x64, 0xb8, 0xb3, 0x04, 0xce, 0x6c, 0x1e, 0xff, 0x44, 0x58, 0xf0, 0xd1, - 0xb4, 0xe2, 0x25, 0xa3, 0xa3, 0xf8, 0x92, 0xa6, 0x9a, 0x4b, 0x15, 0x6c, 0x74, 0x9a, 0xdd, 0xcd, - 0x7e, 0xb4, 0x18, 0xf0, 0xdf, 0xdd, 0xe1, 0xbb, 0x9c, 0xe9, 0x62, 0x9c, 0x44, 0x29, 0x2f, 0x49, - 0xca, 0x55, 0xc9, 0x95, 0x7b, 0x3e, 0xa8, 0xec, 0x8a, 0xe8, 0xa9, 0x00, 0x15, 0x9d, 0x42, 0x3a, - 0x6c, 0xaf, 0x94, 0xbe, 0x58, 0x21, 0x9c, 0xa3, 0xfd, 0xd5, 0x2c, 0x19, 0x53, 0x5a, 0xb2, 0x64, - 0xbc, 0x08, 0xcc, 0x16, 0x5b, 0xbd, 0xe3, 0x5a, 0x83, 0xce, 0x1f, 0x82, 0xd3, 0xb5, 0x0a, 0xe7, - 0xd7, 0x1e, 0xab, 0x03, 0xf1, 0x31, 0x6a, 0x5b, 0x7b, 0x62, 0x01, 0x32, 0x76, 0x4e, 0xb6, 0x8c, - 0x53, 0xcf, 0x2d, 0x30, 0x00, 0x39, 0xb0, 0x96, 0x76, 0xd1, 0x8e, 0x25, 0x58, 0xf2, 0x14, 0xa8, - 0x0c, 0x1e, 0x19, 0xea, 0xb6, 0xcb, 0x0f, 0x40, 0x7e, 0x07, 0x2a, 0xf1, 0x2b, 0x84, 0x4a, 0x3a, - 0x79, 0x90, 0xf3, 0x0d, 0x67, 0xb3, 0xa4, 0x13, 0x27, 0xd4, 0x43, 0x7b, 0x05, 0x55, 0xf1, 0x6a, - 0x43, 0xa5, 0xa9, 0xd4, 0x90, 0x05, 0x8f, 0x8d, 0xdb, 0xbb, 0x05, 0x55, 0xcb, 0x55, 0x2e, 0x2c, - 0xd4, 0x3f, 0xbf, 0x99, 0x85, 0xde, 0xed, 0x2c, 0xf4, 0xfe, 0xcf, 0x42, 0xef, 0xf7, 0x3c, 0x6c, - 0xdc, 0xce, 0xc3, 0xc6, 0xdf, 0x79, 0xd8, 0xf8, 0x41, 0xd6, 0x6c, 0xfe, 0x66, 0x5c, 0xf9, 0x5c, - 0x50, 0x56, 0x11, 0x77, 0x89, 0x93, 0xb5, 0x5b, 0x34, 0x9e, 0x27, 0xbe, 0xb9, 0xc2, 0x8f, 0xf7, - 0x01, 0x00, 0x00, 0xff, 0xff, 0xc8, 0x43, 0x3a, 0xc4, 0xfa, 0x02, 0x00, 0x00, + // 464 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x52, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0x8e, 0x9b, 0x60, 0xe8, 0x16, 0x4a, 0xb3, 0xa5, 0x95, 0x55, 0x84, 0x1b, 0x8a, 0x40, 0x51, + 0x11, 0xb6, 0x6a, 0x4e, 0x5c, 0x43, 0x0b, 0x42, 0x42, 0x28, 0x4a, 0x4f, 0x20, 0x21, 0x6b, 0x6d, + 0x4f, 0xed, 0x55, 0x63, 0xef, 0x6a, 0x77, 0x13, 0x25, 0x6f, 0xc0, 0x91, 0xa7, 0xe0, 0x59, 0x7a, + 0xec, 0x11, 0x71, 0xa8, 0x50, 0xf2, 0x22, 0x55, 0x76, 0x37, 0x3f, 0x07, 0x9f, 0x76, 0x67, 0xbe, + 0x6f, 0xbe, 0x99, 0xf9, 0x34, 0xe8, 0x65, 0x45, 0x13, 0x2a, 0x46, 0x21, 0xad, 0xae, 0x86, 0x44, + 0x51, 0x56, 0x85, 0xe3, 0xb3, 0x30, 0x87, 0x0a, 0x24, 0x95, 0x01, 0x17, 0x4c, 0x31, 0xbc, 0x6f, + 0x28, 0xc1, 0x8a, 0x12, 0x8c, 0xcf, 0x8e, 0x9e, 0xe5, 0x2c, 0x67, 0x1a, 0x0f, 0x17, 0x3f, 0x43, + 0x3d, 0x7a, 0x55, 0xa7, 0xb6, 0xae, 0xd3, 0xa4, 0x93, 0x5f, 0x0e, 0x7a, 0xfc, 0xd9, 0x74, 0xb8, + 0x54, 0x44, 0x01, 0xfe, 0x80, 0x5c, 0x4e, 0x04, 0x29, 0xa5, 0xe7, 0x74, 0x9c, 0xee, 0x4e, 0xf4, + 0x3c, 0xa8, 0xe9, 0x18, 0xf4, 0x35, 0xa5, 0xd7, 0xba, 0xb9, 0x3b, 0x6e, 0x0c, 0x6c, 0x01, 0x3e, + 0x44, 0x2e, 0x07, 0x41, 0x59, 0xe6, 0x6d, 0x75, 0x9c, 0x6e, 0x6b, 0x60, 0x23, 0xfc, 0x1a, 0xed, + 0xca, 0x6b, 0xca, 0x39, 0x64, 0x31, 0x70, 0x96, 0x16, 0xd2, 0x6b, 0x6a, 0xfc, 0x89, 0xcd, 0x5e, + 0xe8, 0xe4, 0xc9, 0x9f, 0x26, 0x72, 0x8d, 0x2e, 0x7e, 0x8b, 0xda, 0xab, 0x76, 0x31, 0x54, 0x24, + 0x19, 0x42, 0xa6, 0xe7, 0x79, 0x34, 0xd8, 0x5b, 0x01, 0x17, 0x26, 0x8f, 0x7f, 0x22, 0xcc, 0xd9, + 0x70, 0x5a, 0xb1, 0x92, 0x92, 0x61, 0x7c, 0x45, 0x52, 0xc5, 0x84, 0xf4, 0xb6, 0x3a, 0xcd, 0xee, + 0x76, 0x2f, 0x58, 0x0c, 0xf8, 0xef, 0xee, 0xf8, 0x4d, 0x4e, 0x55, 0x31, 0x4a, 0x82, 0x94, 0x95, + 0x61, 0xca, 0x64, 0xc9, 0xa4, 0x7d, 0xde, 0xc9, 0xec, 0x3a, 0x54, 0x53, 0x0e, 0x32, 0x38, 0x87, + 0x74, 0xd0, 0x5e, 0x2b, 0x7d, 0x32, 0x42, 0x38, 0x47, 0x87, 0xeb, 0x59, 0x32, 0x2a, 0x95, 0xa0, + 0xc9, 0x68, 0x11, 0xe8, 0x2d, 0x76, 0xa2, 0xd3, 0x5a, 0x83, 0xbe, 0x2c, 0x83, 0xf3, 0x8d, 0x0a, + 0xeb, 0xd7, 0x01, 0xad, 0x03, 0xf1, 0x29, 0x6a, 0x1b, 0x7b, 0x62, 0x0e, 0x22, 0xb6, 0x4e, 0xb6, + 0xb4, 0x53, 0x4f, 0x0d, 0xd0, 0x07, 0xd1, 0x37, 0x96, 0x76, 0xd1, 0x9e, 0x21, 0x18, 0xf2, 0x14, + 0x88, 0xf0, 0x1e, 0x68, 0xea, 0xae, 0xcd, 0xf7, 0x41, 0x7c, 0x07, 0x22, 0xf0, 0x0b, 0x84, 0x4a, + 0x32, 0x59, 0xca, 0xb9, 0x9a, 0xb3, 0x5d, 0x92, 0x89, 0x15, 0x8a, 0xd0, 0x41, 0x41, 0x64, 0xbc, + 0xde, 0x50, 0x2a, 0x22, 0x14, 0x64, 0xde, 0x43, 0xed, 0xf6, 0x7e, 0x41, 0xe4, 0x6a, 0x95, 0x4b, + 0x03, 0xf5, 0xbe, 0xde, 0xcc, 0x7c, 0xe7, 0x76, 0xe6, 0x3b, 0xff, 0x67, 0xbe, 0xf3, 0x7b, 0xee, + 0x37, 0x6e, 0xe7, 0x7e, 0xe3, 0xef, 0xdc, 0x6f, 0xfc, 0x88, 0x36, 0x6c, 0xfe, 0xa6, 0x5d, 0xf9, + 0x58, 0x10, 0x5a, 0x85, 0xf6, 0x12, 0xc7, 0x51, 0x38, 0xd9, 0x38, 0x47, 0x6d, 0x7b, 0xe2, 0xea, + 0x43, 0x7c, 0x7f, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xc4, 0xaa, 0x78, 0xac, 0xfd, 0x02, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { diff --git a/x/inflation/types/inflation.pb.go b/x/inflation/types/inflation.pb.go index 632c86ebf..b1970945b 100644 --- a/x/inflation/types/inflation.pb.go +++ b/x/inflation/types/inflation.pb.go @@ -81,7 +81,7 @@ func init() { } var fileDescriptor_37da805e9a324a97 = []byte{ - // 284 bytes of a gzipped FileDescriptorProto + // 287 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xce, 0xcb, 0x4c, 0xca, 0x2c, 0x2a, 0xd5, 0xcf, 0xcc, 0x4b, 0xcb, 0x49, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x2f, 0x33, 0x44, 0x70, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x84, 0x21, 0x8a, 0xf4, 0x10, 0xe2, 0x65, 0x86, @@ -95,11 +95,11 @@ var fileDescriptor_37da805e9a324a97 = []byte{ 0xdc, 0xdc, 0xd2, 0xbc, 0xcc, 0x92, 0xca, 0xf8, 0x82, 0xfc, 0xfc, 0x1c, 0x09, 0x26, 0xb2, 0xcc, 0xe5, 0x85, 0x9b, 0x12, 0x90, 0x9f, 0x9f, 0x23, 0x14, 0xcb, 0x25, 0x54, 0x5c, 0x52, 0x94, 0x58, 0x92, 0x9a, 0x9e, 0x99, 0x1c, 0x5f, 0x94, 0x5a, 0x9c, 0x5a, 0x54, 0x96, 0x5a, 0x2c, 0xc1, 0x4c, - 0x96, 0xd1, 0x82, 0x70, 0x93, 0x82, 0xa0, 0x06, 0x39, 0x79, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, + 0x96, 0xd1, 0x82, 0x70, 0x93, 0x82, 0xa0, 0x06, 0x39, 0xf9, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, - 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x3e, 0x92, 0xa1, 0x7e, 0xe0, 0x80, 0x77, 0xce, 0x48, 0xcc, 0xcc, - 0xd3, 0x87, 0xc6, 0x54, 0x05, 0x52, 0x5c, 0x81, 0x6d, 0x48, 0x62, 0x03, 0x07, 0xbd, 0x31, 0x20, - 0x00, 0x00, 0xff, 0xff, 0x75, 0xbf, 0x6b, 0xae, 0xcc, 0x01, 0x00, 0x00, + 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x11, 0x92, 0xa1, 0x7e, 0xe0, 0x80, 0x77, 0xce, 0x48, 0xcc, 0xcc, + 0xd3, 0x87, 0xc6, 0x54, 0x99, 0x91, 0x7e, 0x05, 0x52, 0x74, 0x81, 0x2d, 0x49, 0x62, 0x03, 0x87, + 0xbe, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x3e, 0x08, 0x9b, 0xc5, 0xcf, 0x01, 0x00, 0x00, } func (m *InflationDistribution) Marshal() (dAtA []byte, err error) { diff --git a/x/inflation/types/inflation_calculation.go b/x/inflation/types/inflation_calculation.go index 15181166c..b62eeb222 100644 --- a/x/inflation/types/inflation_calculation.go +++ b/x/inflation/types/inflation_calculation.go @@ -1,6 +1,7 @@ package types import ( + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -10,30 +11,31 @@ func CalculateEpochMintProvision( period uint64, ) sdk.Dec { if params.EpochsPerPeriod == 0 || !params.InflationEnabled || period >= params.MaxPeriod { - return sdk.ZeroDec() + return math.LegacyZeroDec() } // truncating to the nearest integer x := period // Calculate the value of the polynomial at x - polynomialValue := polynomial(params.PolynomialFactors, sdk.NewDec(int64(x))) + polynomialValue := polynomial(params.PolynomialFactors, math.LegacyNewDec(int64(x))) if polynomialValue.IsNegative() { // Just to make sure nothing weird occur - return sdk.ZeroDec() + return math.LegacyZeroDec() } - return polynomialValue.Quo(sdk.NewDec(int64(params.EpochsPerPeriod))) + return polynomialValue.Quo(math.LegacyNewDec(int64(params.EpochsPerPeriod))) } // Compute the value of x given the polynomial factors func polynomial(factors []sdk.Dec, x sdk.Dec) sdk.Dec { - result := sdk.ZeroDec() + result := math.LegacyZeroDec() for i, factor := range factors { result = result.Add(factor.Mul(x.Power(uint64(len(factors) - i - 1)))) } // Multiply by 1 million to get the value in unibi - return result.Mul(sdk.NewDec(1_000_000)) + // 1 unibi = 1e6 nibi and the polynomial was fit on nibi token curve. + return result.Mul(math.LegacyNewDec(1_000_000)) } diff --git a/x/inflation/types/inflation_calculation_test.go b/x/inflation/types/inflation_calculation_test.go index 72a87580d..e0782798f 100644 --- a/x/inflation/types/inflation_calculation_test.go +++ b/x/inflation/types/inflation_calculation_test.go @@ -4,20 +4,22 @@ import ( fmt "fmt" "testing" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" ) // These numbers are for year n month 1 var ExpectedYearlyInflation = []sdk.Dec{ - sdk.NewDec(193_333_719e6), - sdk.NewDec(154_304_107e6), - sdk.NewDec(123_153_673e6), - sdk.NewDec(98_291_791e6), - sdk.NewDec(78_448_949e6), - sdk.NewDec(62_611_919e6), - sdk.NewDec(49_972_019e6), - sdk.NewDec(39_883_823e6), + math.LegacyNewDec(193_333_719e6), + math.LegacyNewDec(154_304_107e6), + math.LegacyNewDec(123_153_673e6), + math.LegacyNewDec(98_291_791e6), + math.LegacyNewDec(78_448_949e6), + math.LegacyNewDec(62_611_919e6), + math.LegacyNewDec(49_972_019e6), + math.LegacyNewDec(39_883_823e6), } // ExpectedTotalInflation is the total amount of NIBI tokens (in unibi) that @@ -27,19 +29,19 @@ var ExpectedYearlyInflation = []sdk.Dec{ // = (60% of the total supply) - (Community supply at start) // = (60% of 1.5 billion) - (Community supply at start) // = 800 million NIBI -var ExpectedTotalInflation = sdk.NewDec(800_000_000e6) +var ExpectedTotalInflation = math.LegacyNewDec(800_000_000e6) func TestCalculateEpochMintProvision(t *testing.T) { params := DefaultParams() params.InflationEnabled = true period := uint64(0) - totalInflation := sdk.ZeroDec() + totalInflation := math.LegacyZeroDec() - // Only the first 8 years have inflation with default params but we run + // Only the first 8 years have inflation with default params, but we run // for 10 years expecting 0 inflation in the last 2 years. for year := uint64(0); year < 10; year++ { - yearlyInflation := sdk.ZeroDec() + yearlyInflation := math.LegacyZeroDec() for month := uint64(0); month < 12; month++ { for day := uint64(0); day < 30; day++ { epochMintProvisions := CalculateEpochMintProvision(params, period) @@ -51,7 +53,7 @@ func TestCalculateEpochMintProvision(t *testing.T) { if year < uint64(len(ExpectedYearlyInflation)) { require.NoError(t, withinRange(ExpectedYearlyInflation[year], yearlyInflation)) } else { - require.Equal(t, yearlyInflation, sdk.ZeroDec()) + require.Equal(t, yearlyInflation, math.LegacyZeroDec()) } totalInflation = totalInflation.Add(yearlyInflation) } @@ -63,12 +65,12 @@ func TestCalculateEpochMintProvisionInflationNotEnabled(t *testing.T) { params.InflationEnabled = false epochId := uint64(0) - totalInflation := sdk.ZeroDec() + totalInflation := math.LegacyZeroDec() - // Only the first 8 years have inflation with default params but we run + // Only the first 8 years have inflation with default params, but we run // for 10 years expecting 0 inflation for year := uint64(0); year < 10; year++ { - yearlyInflation := sdk.ZeroDec() + yearlyInflation := math.LegacyZeroDec() for month := uint64(0); month < 12; month++ { for day := uint64(0); day < 30; day++ { epochMintProvisions := CalculateEpochMintProvision(params, epochId) @@ -77,10 +79,10 @@ func TestCalculateEpochMintProvisionInflationNotEnabled(t *testing.T) { epochId++ } - require.Equal(t, yearlyInflation, sdk.ZeroDec()) + require.Equal(t, yearlyInflation, math.LegacyZeroDec()) totalInflation = totalInflation.Add(yearlyInflation) } - require.Equal(t, totalInflation, sdk.ZeroDec()) + require.Equal(t, totalInflation, math.LegacyZeroDec()) } func TestCalculateEpochMintProvision_ZeroEpochs(t *testing.T) { @@ -88,15 +90,15 @@ func TestCalculateEpochMintProvision_ZeroEpochs(t *testing.T) { params.EpochsPerPeriod = 0 epochMintProvisions := CalculateEpochMintProvision(params, 1) - require.Equal(t, epochMintProvisions, sdk.ZeroDec()) + require.Equal(t, epochMintProvisions, math.LegacyZeroDec()) } // withinRange returns an error if the actual value is not within the expected value +/- tolerance // tolerance is a percentage set to 0.01% by default func withinRange(expected, actual sdk.Dec) error { - tolerance := sdk.NewDecWithPrec(1, 4) + tolerance := math.LegacyNewDecWithPrec(1, 4) if expected.Sub(actual).Abs().Quo(expected).GT(tolerance) { - tolerancePercent := tolerance.Mul(sdk.NewDec(100)) + tolerancePercent := tolerance.Mul(math.LegacyNewDec(100)) return fmt.Errorf("expected %s to be within %s%% of %s", actual.String(), tolerancePercent.String(), expected.String()) } return nil diff --git a/x/inflation/types/msgs.go b/x/inflation/types/msgs.go index bae08b89d..cbc4da172 100644 --- a/x/inflation/types/msgs.go +++ b/x/inflation/types/msgs.go @@ -3,6 +3,7 @@ package types import ( "fmt" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" ) @@ -11,12 +12,14 @@ import ( var ( _ legacytx.LegacyMsg = &MsgEditInflationParams{} _ legacytx.LegacyMsg = &MsgToggleInflation{} + _ legacytx.LegacyMsg = &MsgBurn{} ) // oracle message types const ( TypeMsgEditInflationParams = "edit_inflation_params" TypeMsgToggleInflation = "toggle_inflation" + TypeMsgBurn = "msg_burn" ) // Route implements legacytx.LegacyMsg @@ -56,11 +59,11 @@ func (m MsgEditInflationParams) ValidateBasic() error { return fmt.Errorf("inflation distribution strategic reserves should not be nil") } - sum := sdk.NewDec(0) + sum := math.LegacyNewDec(0) sum = sum.Add(m.InflationDistribution.CommunityPool) sum = sum.Add(m.InflationDistribution.StakingRewards) sum = sum.Add(m.InflationDistribution.StrategicReserves) - if !sum.Equal(sdk.OneDec()) { + if !sum.Equal(math.LegacyOneDec()) { return fmt.Errorf("inflation distribution sum should be 1, got %s", sum) } } @@ -103,3 +106,42 @@ func (m MsgToggleInflation) ValidateBasic() error { } return nil } + +// ------------------------------------------------- +// MsgBurn +// Route implements legacytx.LegacyMsg +func (msg MsgBurn) Route() string { return RouterKey } + +// Type implements legacytx.LegacyMsg +func (msg MsgBurn) Type() string { return TypeMsgBurn } + +// GetSignBytes implements legacytx.LegacyMsg +func (msg MsgBurn) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +// GetSigners implements legacytx.LegacyMsg +func (msg MsgBurn) GetSigners() []sdk.AccAddress { + feeder, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + + return []sdk.AccAddress{feeder} +} + +func (m MsgBurn) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return err + } + + if err := m.Coin.Validate(); err != nil { + return err + } + + if m.Coin.Amount.IsZero() { + return fmt.Errorf("coin amount should not be zero") + } + + return nil +} diff --git a/x/inflation/types/params.go b/x/inflation/types/params.go index 0c60f3ab0..6b95bbb7c 100644 --- a/x/inflation/types/params.go +++ b/x/inflation/types/params.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -20,17 +21,17 @@ var ( var ( DefaultInflation = false DefaultPolynomialFactors = []sdk.Dec{ - sdk.MustNewDecFromStr("-0.000147085524"), - sdk.MustNewDecFromStr("0.074291982762"), - sdk.MustNewDecFromStr("-18.867415611180"), - sdk.MustNewDecFromStr("3128.641926954698"), - sdk.MustNewDecFromStr("-334834.740631598223"), - sdk.MustNewDecFromStr("17827464.906540066004"), + math.LegacyMustNewDecFromStr("-0.000147085524"), + math.LegacyMustNewDecFromStr("0.074291982762"), + math.LegacyMustNewDecFromStr("-18.867415611180"), + math.LegacyMustNewDecFromStr("3128.641926954698"), + math.LegacyMustNewDecFromStr("-334834.740631598223"), + math.LegacyMustNewDecFromStr("17827464.906540066004"), } DefaultInflationDistribution = InflationDistribution{ - CommunityPool: sdk.NewDecWithPrec(35_4825, 6), // 35.4825% - StakingRewards: sdk.NewDecWithPrec(28_1250, 6), // 28.1250% - StrategicReserves: sdk.NewDecWithPrec(36_3925, 6), // 36.3925% + CommunityPool: math.LegacyNewDecWithPrec(35_4825, 6), // 35.4825% + StakingRewards: math.LegacyNewDecWithPrec(28_1250, 6), // 28.1250% + StrategicReserves: math.LegacyNewDecWithPrec(36_3925, 6), // 36.3925% } DefaultEpochsPerPeriod = uint64(30) DefaultPeriodsPerYear = uint64(12) @@ -57,7 +58,7 @@ func NewParams( } } -// default minting module parameters +// default inflation module parameters func DefaultParams() Params { return Params{ PolynomialFactors: DefaultPolynomialFactors, @@ -70,7 +71,7 @@ func DefaultParams() Params { } } -func validatePolynomialFactors(i interface{}) error { +func validatePolynomialFactors(i any) error { v, ok := i.([]sdk.Dec) if !ok { return fmt.Errorf("invalid parameter type: %T", i) @@ -82,7 +83,7 @@ func validatePolynomialFactors(i interface{}) error { return nil } -func validateInflationDistribution(i interface{}) error { +func validateInflationDistribution(i any) error { v, ok := i.(InflationDistribution) if !ok { return fmt.Errorf("invalid parameter type: %T", i) @@ -101,14 +102,14 @@ func validateInflationDistribution(i interface{}) error { } totalProportions := v.StakingRewards.Add(v.StrategicReserves).Add(v.CommunityPool) - if !totalProportions.Equal(sdk.OneDec()) { + if !totalProportions.Equal(math.LegacyOneDec()) { return errors.New("total distributions ratio should be 1") } return nil } -func validateBool(i interface{}) error { +func validateBool(i any) error { _, ok := i.(bool) if !ok { return fmt.Errorf("invalid parameter type: %T", i) @@ -117,7 +118,7 @@ func validateBool(i interface{}) error { return nil } -func validateUint64(i interface{}) error { +func validateUint64(i any) error { _, ok := i.(uint64) if !ok { return fmt.Errorf("invalid genesis state type: %T", i) @@ -125,7 +126,7 @@ func validateUint64(i interface{}) error { return nil } -func validateEpochsPerPeriod(i interface{}) error { +func validateEpochsPerPeriod(i any) error { val, ok := i.(uint64) if !ok { return fmt.Errorf("invalid parameter type: %T", i) @@ -138,7 +139,7 @@ func validateEpochsPerPeriod(i interface{}) error { return nil } -func validatePeriodsPerYear(i interface{}) error { +func validatePeriodsPerYear(i any) error { val, ok := i.(uint64) if !ok { return fmt.Errorf("invalid parameter type: %T", i) diff --git a/x/inflation/types/params_test.go b/x/inflation/types/params_test.go index 4f668b087..d2d0fd91b 100644 --- a/x/inflation/types/params_test.go +++ b/x/inflation/types/params_test.go @@ -3,7 +3,9 @@ package types_test import ( "testing" - inflationtypes "github.com/NibiruChain/nibiru/x/inflation/types" + "cosmossdk.io/math" + + inflationtypes "github.com/NibiruChain/nibiru/v2/x/inflation/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -62,9 +64,9 @@ func TestParamsValidate(t *testing.T) { inflationtypes.Params{ PolynomialFactors: inflationtypes.DefaultPolynomialFactors, InflationDistribution: inflationtypes.InflationDistribution{ - StakingRewards: sdk.OneDec().Neg(), - CommunityPool: sdk.NewDecWithPrec(133333, 6), - StrategicReserves: sdk.NewDecWithPrec(333333, 6), + StakingRewards: math.LegacyOneDec().Neg(), + CommunityPool: math.LegacyNewDecWithPrec(133333, 6), + StrategicReserves: math.LegacyNewDecWithPrec(333333, 6), }, InflationEnabled: true, HasInflationStarted: true, @@ -78,9 +80,9 @@ func TestParamsValidate(t *testing.T) { inflationtypes.Params{ PolynomialFactors: inflationtypes.DefaultPolynomialFactors, InflationDistribution: inflationtypes.InflationDistribution{ - StakingRewards: sdk.NewDecWithPrec(533334, 6), - CommunityPool: sdk.NewDecWithPrec(133333, 6), - StrategicReserves: sdk.OneDec().Neg(), + StakingRewards: math.LegacyNewDecWithPrec(533334, 6), + CommunityPool: math.LegacyNewDecWithPrec(133333, 6), + StrategicReserves: math.LegacyOneDec().Neg(), }, InflationEnabled: true, HasInflationStarted: true, @@ -94,9 +96,9 @@ func TestParamsValidate(t *testing.T) { inflationtypes.Params{ PolynomialFactors: inflationtypes.DefaultPolynomialFactors, InflationDistribution: inflationtypes.InflationDistribution{ - StakingRewards: sdk.NewDecWithPrec(533334, 6), - CommunityPool: sdk.OneDec().Neg(), - StrategicReserves: sdk.NewDecWithPrec(333333, 6), + StakingRewards: math.LegacyNewDecWithPrec(533334, 6), + CommunityPool: math.LegacyOneDec().Neg(), + StrategicReserves: math.LegacyNewDecWithPrec(333333, 6), }, InflationEnabled: true, HasInflationStarted: true, @@ -110,9 +112,9 @@ func TestParamsValidate(t *testing.T) { inflationtypes.Params{ PolynomialFactors: inflationtypes.DefaultPolynomialFactors, InflationDistribution: inflationtypes.InflationDistribution{ - StakingRewards: sdk.NewDecWithPrec(533333, 6), - CommunityPool: sdk.NewDecWithPrec(133333, 6), - StrategicReserves: sdk.NewDecWithPrec(333333, 6), + StakingRewards: math.LegacyNewDecWithPrec(533333, 6), + CommunityPool: math.LegacyNewDecWithPrec(133333, 6), + StrategicReserves: math.LegacyNewDecWithPrec(333333, 6), }, InflationEnabled: true, HasInflationStarted: true, diff --git a/x/inflation/types/query.pb.go b/x/inflation/types/query.pb.go index 4b15226dc..609ff196e 100644 --- a/x/inflation/types/query.pb.go +++ b/x/inflation/types/query.pb.go @@ -549,50 +549,51 @@ func init() { func init() { proto.RegisterFile("nibiru/inflation/v1/query.proto", fileDescriptor_9cef9ea5e4d20e5e) } var fileDescriptor_9cef9ea5e4d20e5e = []byte{ - // 686 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0x4f, 0x4f, 0x14, 0x3d, - 0x1c, 0xc7, 0xb7, 0x4f, 0x78, 0xd6, 0x58, 0x03, 0x09, 0x85, 0x18, 0x1d, 0x60, 0x16, 0x97, 0x20, - 0x10, 0x42, 0x9b, 0x65, 0xbd, 0x78, 0x65, 0xf5, 0xc0, 0x41, 0x83, 0x43, 0xbc, 0x78, 0xd9, 0xcc, - 0x0e, 0x75, 0x68, 0x60, 0xdb, 0x32, 0x9d, 0xd9, 0xc8, 0xcd, 0xe8, 0x1b, 0x30, 0xf1, 0xec, 0xc9, - 0x83, 0x89, 0x89, 0x17, 0x5e, 0x05, 0x47, 0x12, 0x2f, 0xc6, 0x03, 0x1a, 0xf0, 0x85, 0x98, 0xe9, - 0x74, 0x96, 0x1d, 0xb6, 0xb3, 0xc0, 0xc1, 0xd3, 0xce, 0xf6, 0xf7, 0xef, 0xdb, 0x6f, 0xfb, 0x99, - 0x81, 0x35, 0xce, 0x3a, 0x2c, 0x4a, 0x08, 0xe3, 0xaf, 0xf7, 0xfd, 0x98, 0x09, 0x4e, 0x7a, 0x0d, - 0x72, 0x90, 0xd0, 0xe8, 0x10, 0xcb, 0x48, 0xc4, 0x02, 0x4d, 0x65, 0x09, 0xb8, 0x9f, 0x80, 0x7b, - 0x0d, 0xc7, 0x0d, 0x84, 0xea, 0x0a, 0x45, 0x3a, 0xbe, 0xa2, 0xa4, 0xd7, 0xe8, 0xd0, 0xd8, 0x6f, - 0x90, 0x40, 0x30, 0x9e, 0x15, 0x39, 0x0f, 0x6c, 0x5d, 0x43, 0xca, 0xa9, 0x62, 0xca, 0xa4, 0x4c, - 0x87, 0x22, 0x14, 0xfa, 0x91, 0xa4, 0x4f, 0x66, 0x75, 0x36, 0x14, 0x22, 0xdc, 0xa7, 0xc4, 0x97, - 0x8c, 0xf8, 0x9c, 0x8b, 0x58, 0x57, 0x9b, 0x9a, 0xfa, 0x34, 0x44, 0x2f, 0x52, 0x69, 0x5b, 0x34, - 0x62, 0x62, 0xc7, 0xa3, 0x07, 0x09, 0x55, 0x71, 0x7d, 0x0d, 0x4e, 0x15, 0x56, 0x95, 0x14, 0x5c, - 0x51, 0x74, 0x17, 0x56, 0xa5, 0x5e, 0xb9, 0x07, 0xe6, 0xc1, 0xf2, 0x98, 0x67, 0xfe, 0xd5, 0xe7, - 0xa1, 0xab, 0xd3, 0x9f, 0x4a, 0x11, 0xec, 0x3e, 0x63, 0x3c, 0xde, 0x8a, 0x44, 0x8f, 0x29, 0x26, - 0x78, 0xde, 0xf0, 0x0b, 0x80, 0xb5, 0xd2, 0x14, 0xd3, 0xfd, 0x3d, 0x80, 0xd3, 0x34, 0x0d, 0xb7, - 0xbb, 0x8c, 0xc7, 0x6d, 0x99, 0x27, 0xe8, 0x61, 0x77, 0xd6, 0x67, 0x71, 0xe6, 0x10, 0x4e, 0x1d, - 0xc2, 0xc6, 0x21, 0xfc, 0x84, 0x06, 0x2d, 0xc1, 0xf8, 0x46, 0xf3, 0xf8, 0xb4, 0x56, 0xf9, 0xfa, - 0xab, 0xb6, 0x1a, 0xb2, 0x78, 0x37, 0xe9, 0xe0, 0x40, 0x74, 0x89, 0x71, 0x34, 0xfb, 0x59, 0x53, - 0x3b, 0x7b, 0x24, 0x3e, 0x94, 0x54, 0xe5, 0x35, 0xca, 0x43, 0x74, 0x48, 0x4d, 0x7d, 0x06, 0xde, - 0xd7, 0x42, 0xb7, 0xf7, 0x98, 0x94, 0x74, 0x47, 0xeb, 0x55, 0xf9, 0x36, 0x5a, 0xd0, 0xb1, 0x05, - 0xcd, 0x06, 0x16, 0xe1, 0x84, 0xca, 0x02, 0x6d, 0xdd, 0x58, 0x19, 0x9b, 0xc6, 0xd5, 0x60, 0x7a, - 0xbd, 0x06, 0xe7, 0x74, 0x93, 0x16, 0x8b, 0x82, 0x24, 0x3d, 0x4b, 0x1e, 0x6e, 0x27, 0x52, 0xee, - 0x1f, 0xe6, 0x53, 0x3e, 0x03, 0xe3, 0xa7, 0x25, 0xc3, 0x8c, 0x7a, 0x0b, 0x20, 0x0a, 0x2e, 0xa2, - 0x6d, 0xa5, 0xc3, 0xff, 0xce, 0xa9, 0xc9, 0xe0, 0xb2, 0x94, 0xbe, 0x51, 0x9b, 0xf9, 0x85, 0xf4, - 0xfc, 0x98, 0xe6, 0x5b, 0x50, 0xc6, 0xa8, 0x4b, 0x41, 0xa3, 0xfe, 0x25, 0x9c, 0xe8, 0x5f, 0xe3, - 0x76, 0xe4, 0xc7, 0x54, 0x0b, 0xbf, 0xbd, 0x81, 0x53, 0x69, 0x3f, 0x4f, 0x6b, 0x0f, 0xaf, 0x27, - 0xcd, 0x1b, 0x67, 0x83, 0xed, 0x2f, 0xee, 0xb2, 0x1f, 0xf9, 0xdd, 0xfe, 0x99, 0x6d, 0xe5, 0x77, - 0xd9, 0xac, 0x1a, 0x0d, 0x8f, 0x61, 0x55, 0xea, 0x15, 0x63, 0xda, 0x0c, 0xb6, 0x50, 0x89, 0xb3, - 0xa2, 0x8d, 0xb1, 0x54, 0x98, 0x67, 0x0a, 0xd6, 0x8f, 0x6e, 0xc1, 0xff, 0x75, 0xcb, 0xf4, 0x18, - 0xaa, 0x19, 0x23, 0x68, 0xc9, 0x5a, 0x3f, 0xcc, 0x96, 0xb3, 0x7c, 0x75, 0x62, 0x26, 0xb1, 0xbe, - 0xf0, 0xee, 0xfb, 0x9f, 0x8f, 0xff, 0xcd, 0xa1, 0x19, 0x62, 0x63, 0x3f, 0x63, 0x0f, 0x1d, 0x01, - 0x88, 0x86, 0xa1, 0x42, 0xcd, 0xf2, 0x29, 0xa5, 0x94, 0x3a, 0x8f, 0x6e, 0x56, 0x64, 0x64, 0x36, - 0xb4, 0xcc, 0x55, 0xb4, 0x62, 0x95, 0x69, 0x23, 0x1a, 0x7d, 0x02, 0x70, 0xbc, 0xc0, 0x10, 0xc2, - 0xe5, 0xa3, 0x6d, 0x24, 0x3a, 0xe4, 0xda, 0xf9, 0x46, 0xe5, 0xaa, 0x56, 0xb9, 0x88, 0x16, 0xac, - 0x2a, 0x8b, 0xdc, 0xa2, 0x6f, 0x00, 0x4e, 0x0e, 0xc1, 0x87, 0xd6, 0xcb, 0x67, 0x96, 0xb1, 0xec, - 0x34, 0x6f, 0x54, 0x63, 0xb4, 0x12, 0xad, 0x75, 0x05, 0x2d, 0x59, 0xb5, 0x0e, 0x73, 0xaf, 0xfd, - 0x2c, 0xa0, 0x36, 0xca, 0x4f, 0x1b, 0xb0, 0xa3, 0xfc, 0xb4, 0x32, 0x7c, 0x85, 0x9f, 0x45, 0xbc, - 0x33, 0x4e, 0x34, 0x3c, 0x23, 0x39, 0x19, 0xe4, 0x76, 0x24, 0x27, 0x05, 0x94, 0xaf, 0xe2, 0x24, - 0x43, 0x78, 0xf3, 0xf8, 0xcc, 0x05, 0x27, 0x67, 0x2e, 0xf8, 0x7d, 0xe6, 0x82, 0x0f, 0xe7, 0x6e, - 0xe5, 0xe4, 0xdc, 0xad, 0xfc, 0x38, 0x77, 0x2b, 0xaf, 0xc8, 0xc0, 0xdb, 0xe6, 0xb9, 0x6e, 0xd0, - 0xda, 0xf5, 0x19, 0xcf, 0x9b, 0xbd, 0x19, 0x68, 0xa7, 0x5f, 0x3d, 0x9d, 0xaa, 0xfe, 0x74, 0x36, - 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x40, 0x82, 0x21, 0x19, 0xe9, 0x07, 0x00, 0x00, + // 689 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0x4f, 0x4f, 0x13, 0x4d, + 0x1c, 0xc7, 0x3b, 0x4f, 0x78, 0x6a, 0x1c, 0x03, 0x09, 0x03, 0x31, 0xba, 0xc0, 0x16, 0x4b, 0x10, + 0x08, 0x61, 0x26, 0x6d, 0xbd, 0x78, 0xa5, 0x7a, 0x30, 0x51, 0x83, 0x25, 0x5e, 0xbc, 0x34, 0xdb, + 0x65, 0x5c, 0x26, 0xd0, 0x99, 0x61, 0x67, 0xb7, 0x91, 0x9b, 0xd1, 0x37, 0x60, 0xe2, 0xd9, 0x93, + 0x07, 0x13, 0x13, 0x2f, 0xbc, 0x0a, 0x8e, 0x24, 0x5e, 0x8c, 0x07, 0x34, 0xe0, 0x0b, 0x31, 0x3b, + 0x3b, 0x5b, 0xba, 0x74, 0xb6, 0x85, 0x83, 0xa7, 0x6e, 0xe7, 0xf7, 0xef, 0x3b, 0xdf, 0x99, 0xcf, + 0x2e, 0xac, 0x70, 0xd6, 0x61, 0x61, 0x4c, 0x18, 0x7f, 0xbd, 0xef, 0x45, 0x4c, 0x70, 0xd2, 0xab, + 0x91, 0x83, 0x98, 0x86, 0x87, 0x58, 0x86, 0x22, 0x12, 0x68, 0x26, 0x4d, 0xc0, 0xfd, 0x04, 0xdc, + 0xab, 0x39, 0xae, 0x2f, 0x54, 0x57, 0x28, 0xd2, 0xf1, 0x14, 0x25, 0xbd, 0x5a, 0x87, 0x46, 0x5e, + 0x8d, 0xf8, 0x82, 0xf1, 0xb4, 0xc8, 0xb9, 0x67, 0xeb, 0x1a, 0x50, 0x4e, 0x15, 0x53, 0x26, 0x65, + 0x36, 0x10, 0x81, 0xd0, 0x8f, 0x24, 0x79, 0x32, 0xab, 0xf3, 0x81, 0x10, 0xc1, 0x3e, 0x25, 0x9e, + 0x64, 0xc4, 0xe3, 0x5c, 0x44, 0xba, 0xda, 0xd4, 0x54, 0x67, 0x21, 0x7a, 0x91, 0x48, 0xdb, 0xa2, + 0x21, 0x13, 0x3b, 0x2d, 0x7a, 0x10, 0x53, 0x15, 0x55, 0x37, 0xe0, 0x4c, 0x6e, 0x55, 0x49, 0xc1, + 0x15, 0x45, 0xb7, 0x61, 0x59, 0xea, 0x95, 0x3b, 0x60, 0x11, 0xac, 0x4e, 0xb4, 0xcc, 0xbf, 0xea, + 0x22, 0x74, 0x75, 0xfa, 0x63, 0x29, 0xfc, 0xdd, 0x67, 0x8c, 0x47, 0x5b, 0xa1, 0xe8, 0x31, 0xc5, + 0x04, 0xcf, 0x1a, 0x7e, 0x01, 0xb0, 0x52, 0x98, 0x62, 0xba, 0xbf, 0x07, 0x70, 0x96, 0x26, 0xe1, + 0x76, 0x97, 0xf1, 0xa8, 0x2d, 0xb3, 0x04, 0x3d, 0xec, 0x56, 0x7d, 0x1e, 0xa7, 0x0e, 0xe1, 0xc4, + 0x21, 0x6c, 0x1c, 0xc2, 0x8f, 0xa8, 0xdf, 0x14, 0x8c, 0x6f, 0x36, 0x8e, 0x4f, 0x2b, 0xa5, 0xaf, + 0xbf, 0x2a, 0xeb, 0x01, 0x8b, 0x76, 0xe3, 0x0e, 0xf6, 0x45, 0x97, 0x18, 0x47, 0xd3, 0x9f, 0x0d, + 0xb5, 0xb3, 0x47, 0xa2, 0x43, 0x49, 0x55, 0x56, 0xa3, 0x5a, 0x88, 0x0e, 0xa9, 0xa9, 0xce, 0xc1, + 0xbb, 0x5a, 0xe8, 0xf6, 0x1e, 0x93, 0x92, 0xee, 0x68, 0xbd, 0x2a, 0xdb, 0x46, 0x13, 0x3a, 0xb6, + 0xa0, 0xd9, 0xc0, 0x32, 0x9c, 0x52, 0x69, 0xa0, 0xad, 0x1b, 0x2b, 0x63, 0xd3, 0xa4, 0x1a, 0x4c, + 0xaf, 0x56, 0xe0, 0x82, 0x6e, 0xd2, 0x64, 0xa1, 0x1f, 0x27, 0x67, 0xc9, 0x83, 0xed, 0x58, 0xca, + 0xfd, 0xc3, 0x6c, 0xca, 0x67, 0x60, 0xfc, 0xb4, 0x64, 0x98, 0x51, 0x6f, 0x01, 0x44, 0xfe, 0x45, + 0xb4, 0xad, 0x74, 0xf8, 0xdf, 0x39, 0x35, 0xed, 0x5f, 0x96, 0xd2, 0x37, 0xea, 0x49, 0x76, 0x21, + 0x5b, 0x5e, 0x44, 0xb3, 0x2d, 0x28, 0x63, 0xd4, 0xa5, 0xa0, 0x51, 0xff, 0x12, 0x4e, 0xf5, 0xaf, + 0x71, 0x3b, 0xf4, 0x22, 0xaa, 0x85, 0xdf, 0xdc, 0xc4, 0x89, 0xb4, 0x9f, 0xa7, 0x95, 0xfb, 0x57, + 0x93, 0xd6, 0x9a, 0x64, 0x83, 0xed, 0x2f, 0xee, 0xb2, 0x17, 0x7a, 0xdd, 0xfe, 0x99, 0x6d, 0x65, + 0x77, 0xd9, 0xac, 0x1a, 0x0d, 0x0f, 0x61, 0x59, 0xea, 0x15, 0x63, 0xda, 0x1c, 0xb6, 0x50, 0x89, + 0xd3, 0xa2, 0xcd, 0x89, 0x44, 0x58, 0xcb, 0x14, 0xd4, 0x8f, 0x6e, 0xc0, 0xff, 0x75, 0xcb, 0xe4, + 0x18, 0xca, 0x29, 0x23, 0x68, 0xc5, 0x5a, 0x3f, 0xcc, 0x96, 0xb3, 0x3a, 0x3e, 0x31, 0x95, 0x58, + 0x5d, 0x7a, 0xf7, 0xfd, 0xcf, 0xc7, 0xff, 0x16, 0xd0, 0x1c, 0xb1, 0xb1, 0x9f, 0xb2, 0x87, 0x8e, + 0x00, 0x44, 0xc3, 0x50, 0xa1, 0x46, 0xf1, 0x94, 0x42, 0x4a, 0x9d, 0x07, 0xd7, 0x2b, 0x32, 0x32, + 0x6b, 0x5a, 0xe6, 0x3a, 0x5a, 0xb3, 0xca, 0xb4, 0x11, 0x8d, 0x3e, 0x01, 0x38, 0x99, 0x63, 0x08, + 0xe1, 0xe2, 0xd1, 0x36, 0x12, 0x1d, 0x72, 0xe5, 0x7c, 0xa3, 0x72, 0x5d, 0xab, 0x5c, 0x46, 0x4b, + 0x56, 0x95, 0x79, 0x6e, 0xd1, 0x37, 0x00, 0xa7, 0x87, 0xe0, 0x43, 0xf5, 0xe2, 0x99, 0x45, 0x2c, + 0x3b, 0x8d, 0x6b, 0xd5, 0x18, 0xad, 0x44, 0x6b, 0x5d, 0x43, 0x2b, 0x56, 0xad, 0xc3, 0xdc, 0x6b, + 0x3f, 0x73, 0xa8, 0x8d, 0xf2, 0xd3, 0x06, 0xec, 0x28, 0x3f, 0xad, 0x0c, 0x8f, 0xf1, 0x33, 0x8f, + 0x77, 0xca, 0x89, 0x86, 0x67, 0x24, 0x27, 0x83, 0xdc, 0x8e, 0xe4, 0x24, 0x87, 0xf2, 0x38, 0x4e, + 0x52, 0x84, 0x9f, 0x1e, 0x9f, 0xb9, 0xe0, 0xe4, 0xcc, 0x05, 0xbf, 0xcf, 0x5c, 0xf0, 0xe1, 0xdc, + 0x2d, 0x9d, 0x9c, 0xbb, 0xa5, 0x1f, 0xe7, 0x6e, 0xe9, 0x55, 0x7d, 0xe0, 0x6d, 0xf3, 0x5c, 0x37, + 0x68, 0xee, 0x7a, 0x8c, 0x67, 0xcd, 0x7a, 0x75, 0xf2, 0x66, 0xa0, 0xa3, 0x7e, 0xfb, 0x74, 0xca, + 0xfa, 0xeb, 0xd9, 0xf8, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xc2, 0xcf, 0xda, 0x1b, 0xec, 0x07, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/inflation/types/tx.pb.go b/x/inflation/types/tx.pb.go index 59513eabe..78d8f2131 100644 --- a/x/inflation/types/tx.pb.go +++ b/x/inflation/types/tx.pb.go @@ -6,8 +6,8 @@ package types import ( context "context" fmt "fmt" - _ "github.com/cosmos/cosmos-sdk/types" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" @@ -185,55 +185,150 @@ func (m *MsgEditInflationParamsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgEditInflationParamsResponse proto.InternalMessageInfo +// MsgBurn: allows burning of any token +type MsgBurn struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + Coin types.Coin `protobuf:"bytes,2,opt,name=coin,proto3" json:"coin" yaml:"coin"` +} + +func (m *MsgBurn) Reset() { *m = MsgBurn{} } +func (m *MsgBurn) String() string { return proto.CompactTextString(m) } +func (*MsgBurn) ProtoMessage() {} +func (*MsgBurn) Descriptor() ([]byte, []int) { + return fileDescriptor_9f6843f876608d76, []int{4} +} +func (m *MsgBurn) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgBurn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgBurn.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgBurn) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgBurn.Merge(m, src) +} +func (m *MsgBurn) XXX_Size() int { + return m.Size() +} +func (m *MsgBurn) XXX_DiscardUnknown() { + xxx_messageInfo_MsgBurn.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgBurn proto.InternalMessageInfo + +func (m *MsgBurn) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgBurn) GetCoin() types.Coin { + if m != nil { + return m.Coin + } + return types.Coin{} +} + +type MsgBurnResponse struct { +} + +func (m *MsgBurnResponse) Reset() { *m = MsgBurnResponse{} } +func (m *MsgBurnResponse) String() string { return proto.CompactTextString(m) } +func (*MsgBurnResponse) ProtoMessage() {} +func (*MsgBurnResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9f6843f876608d76, []int{5} +} +func (m *MsgBurnResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgBurnResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgBurnResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgBurnResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgBurnResponse.Merge(m, src) +} +func (m *MsgBurnResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgBurnResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgBurnResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgBurnResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgToggleInflation)(nil), "nibiru.inflation.v1.MsgToggleInflation") proto.RegisterType((*MsgEditInflationParams)(nil), "nibiru.inflation.v1.MsgEditInflationParams") proto.RegisterType((*MsgToggleInflationResponse)(nil), "nibiru.inflation.v1.MsgToggleInflationResponse") proto.RegisterType((*MsgEditInflationParamsResponse)(nil), "nibiru.inflation.v1.MsgEditInflationParamsResponse") + proto.RegisterType((*MsgBurn)(nil), "nibiru.inflation.v1.MsgBurn") + proto.RegisterType((*MsgBurnResponse)(nil), "nibiru.inflation.v1.MsgBurnResponse") } func init() { proto.RegisterFile("nibiru/inflation/v1/tx.proto", fileDescriptor_9f6843f876608d76) } var fileDescriptor_9f6843f876608d76 = []byte{ - // 597 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0x31, 0x6f, 0xd3, 0x4c, - 0x18, 0xc7, 0xe3, 0xb6, 0x6f, 0xde, 0xf6, 0x10, 0xb4, 0xb9, 0x42, 0x65, 0x85, 0xe0, 0x44, 0xae, - 0x44, 0x53, 0xaa, 0xf8, 0x94, 0x76, 0xeb, 0x18, 0x5a, 0xa4, 0x0c, 0x41, 0x91, 0xc5, 0x00, 0x95, - 0x50, 0x74, 0xb6, 0xaf, 0xce, 0x09, 0xdb, 0x67, 0xf9, 0x2e, 0x51, 0xb2, 0x32, 0x31, 0x56, 0xe2, - 0x0b, 0x74, 0xe4, 0x03, 0xf0, 0x0d, 0x58, 0x3a, 0x56, 0x62, 0x41, 0x0c, 0x11, 0x4a, 0x18, 0x98, - 0xf9, 0x04, 0x28, 0x3e, 0xc7, 0x89, 0x88, 0x2b, 0xd1, 0x0e, 0x51, 0x72, 0xf7, 0xfc, 0x9f, 0xdf, - 0xf3, 0xbf, 0x7b, 0x9e, 0x1c, 0x28, 0x05, 0xd4, 0xa2, 0x51, 0x0f, 0xd1, 0xe0, 0xdc, 0xc3, 0x82, - 0xb2, 0x00, 0xf5, 0xeb, 0x48, 0x0c, 0x8c, 0x30, 0x62, 0x82, 0xc1, 0x6d, 0x19, 0x35, 0xd2, 0xa8, - 0xd1, 0xaf, 0x17, 0x1f, 0xba, 0xcc, 0x65, 0x71, 0x1c, 0x4d, 0x7f, 0x49, 0x69, 0xb1, 0xe4, 0x32, - 0xe6, 0x7a, 0x04, 0xe1, 0x90, 0x22, 0x1c, 0x04, 0x4c, 0xc4, 0x7a, 0x9e, 0x44, 0x77, 0xb3, 0xca, - 0xcc, 0xa9, 0x52, 0xa4, 0xd9, 0x8c, 0xfb, 0x8c, 0x23, 0x0b, 0x73, 0x82, 0xfa, 0x75, 0x8b, 0x08, - 0x5c, 0x47, 0x36, 0xa3, 0x49, 0x5c, 0xc7, 0x00, 0xb6, 0xb8, 0xfb, 0x8a, 0xb9, 0xae, 0x47, 0x9a, - 0xb3, 0x5c, 0xb8, 0x03, 0xf2, 0x9c, 0x04, 0x0e, 0x89, 0x54, 0xa5, 0xa2, 0x54, 0x37, 0xcc, 0x64, - 0x05, 0xf7, 0x41, 0x9e, 0x04, 0xd8, 0xf2, 0x88, 0xba, 0x52, 0x51, 0xaa, 0xeb, 0x8d, 0xc2, 0xef, - 0x51, 0xf9, 0xfe, 0x10, 0xfb, 0xde, 0xb1, 0x2e, 0xf7, 0x75, 0x33, 0x11, 0x1c, 0xaf, 0x7f, 0xb8, - 0x2c, 0xe7, 0x7e, 0x5d, 0x96, 0x73, 0xfa, 0xe7, 0x35, 0xb0, 0xd3, 0xe2, 0xee, 0xa9, 0x43, 0x45, - 0x5a, 0xa1, 0x8d, 0x23, 0xec, 0xf3, 0x1b, 0xeb, 0x1c, 0x80, 0x42, 0x7a, 0x90, 0x8e, 0x04, 0x3a, - 0xb2, 0xa4, 0xb9, 0x95, 0x06, 0x4e, 0xe5, 0x3e, 0x7c, 0x0b, 0x60, 0xc8, 0xbc, 0x61, 0xc0, 0x7c, - 0x8a, 0xbd, 0xce, 0x39, 0xb6, 0x05, 0x8b, 0xb8, 0xba, 0x5a, 0x59, 0xad, 0x6e, 0x34, 0x8c, 0xab, - 0x51, 0x59, 0xf9, 0x3e, 0x2a, 0x3f, 0x75, 0xa9, 0xe8, 0xf6, 0x2c, 0xc3, 0x66, 0x3e, 0x4a, 0x6e, - 0x44, 0x7e, 0xd5, 0xb8, 0xf3, 0x0e, 0x89, 0x61, 0x48, 0xb8, 0x71, 0x42, 0x6c, 0xb3, 0x30, 0x27, - 0xbd, 0x90, 0x20, 0xe8, 0x82, 0x9d, 0xb9, 0x17, 0x87, 0x72, 0x11, 0x51, 0xab, 0x37, 0x5d, 0xa8, - 0x6b, 0x15, 0xa5, 0x7a, 0xef, 0xf0, 0x99, 0x91, 0xd1, 0x50, 0x23, 0x3d, 0xe9, 0xc9, 0x42, 0x46, - 0x63, 0x6d, 0x6a, 0xc7, 0x7c, 0x44, 0xb3, 0x82, 0xf0, 0x0c, 0x14, 0x48, 0xc8, 0xec, 0x2e, 0xef, - 0x84, 0x24, 0x9a, 0x7e, 0x28, 0x73, 0xd4, 0xff, 0xa6, 0xf7, 0x72, 0xab, 0x63, 0x34, 0x03, 0x61, - 0x6e, 0x4a, 0x50, 0x9b, 0x44, 0xed, 0x18, 0x03, 0x5f, 0x83, 0x2d, 0x09, 0x94, 0xf0, 0x21, 0xc1, - 0x91, 0x9a, 0xbf, 0x13, 0xfa, 0x41, 0xc2, 0x69, 0x93, 0xe8, 0x0d, 0xc1, 0x11, 0x6c, 0x01, 0xe0, - 0xe3, 0xc1, 0xcc, 0xee, 0xff, 0x77, 0x62, 0x6e, 0xf8, 0x78, 0x20, 0x8d, 0x2e, 0x8c, 0x4d, 0x09, - 0x14, 0x97, 0x27, 0xd3, 0x24, 0x3c, 0x64, 0x01, 0x27, 0x7a, 0x05, 0x68, 0xd9, 0x33, 0x35, 0x53, - 0x1c, 0x7e, 0x59, 0x01, 0xab, 0x2d, 0xee, 0xc2, 0x0b, 0x05, 0x6c, 0xfe, 0x3d, 0xdf, 0x7b, 0x99, - 0x3d, 0x5b, 0x2e, 0x57, 0x44, 0xff, 0x28, 0x4c, 0x7d, 0xed, 0xbe, 0xff, 0xfa, 0xf3, 0xe3, 0xca, - 0x13, 0xfd, 0x31, 0xca, 0x7c, 0x04, 0xe2, 0x2c, 0xf8, 0x49, 0x01, 0xdb, 0x59, 0x7f, 0x87, 0x83, - 0x9b, 0xaa, 0x65, 0x88, 0x8b, 0x47, 0xb7, 0x10, 0xa7, 0xf6, 0x50, 0x6c, 0x6f, 0x5f, 0xdf, 0x5b, - 0xb6, 0x47, 0x1c, 0x2a, 0x6a, 0xe9, 0xb2, 0x16, 0xc6, 0x89, 0x8d, 0xe6, 0xd5, 0x58, 0x53, 0xae, - 0xc7, 0x9a, 0xf2, 0x63, 0xac, 0x29, 0x17, 0x13, 0x2d, 0x77, 0x3d, 0xd1, 0x72, 0xdf, 0x26, 0x5a, - 0xee, 0x0c, 0x2d, 0x34, 0xf7, 0x65, 0x0c, 0x7b, 0xde, 0xc5, 0x34, 0x98, 0x81, 0x07, 0x0b, 0xe8, - 0xb8, 0xd3, 0x56, 0x3e, 0x7e, 0x71, 0x8e, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0x28, 0x3a, 0x52, - 0x97, 0x1f, 0x05, 0x00, 0x00, + // 664 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xc1, 0x4e, 0xdb, 0x4a, + 0x14, 0x8d, 0x21, 0x2f, 0xc0, 0xa0, 0x57, 0x88, 0x69, 0x51, 0x9a, 0x52, 0x3b, 0x32, 0x52, 0x09, + 0x45, 0x78, 0x94, 0xb0, 0x63, 0x69, 0xa0, 0x12, 0x52, 0x53, 0x45, 0x56, 0x17, 0x2d, 0x52, 0x85, + 0xc6, 0xf1, 0x60, 0x46, 0xb5, 0x67, 0x2c, 0xcf, 0x24, 0x4a, 0xb6, 0x5d, 0x75, 0x89, 0xd4, 0x1f, + 0x60, 0xd9, 0x0f, 0xe8, 0x1f, 0x74, 0xc3, 0x12, 0xa9, 0x9b, 0xaa, 0x8b, 0xa8, 0x82, 0x2e, 0xba, + 0xe6, 0x0b, 0x2a, 0x7b, 0x1c, 0x63, 0x15, 0x23, 0x15, 0x16, 0x96, 0x3d, 0x73, 0xcf, 0x3d, 0xf7, + 0xdc, 0xf1, 0xb9, 0x03, 0x56, 0x28, 0x71, 0x48, 0xd4, 0x87, 0x84, 0x1e, 0xf9, 0x48, 0x10, 0x46, + 0xe1, 0xa0, 0x05, 0xc5, 0xd0, 0x0c, 0x23, 0x26, 0x98, 0xba, 0x24, 0xa3, 0x66, 0x16, 0x35, 0x07, + 0xad, 0xfa, 0x43, 0x8f, 0x79, 0x2c, 0x89, 0xc3, 0xf8, 0x4b, 0x42, 0xeb, 0x2b, 0x1e, 0x63, 0x9e, + 0x8f, 0x21, 0x0a, 0x09, 0x44, 0x94, 0x32, 0x91, 0xe0, 0x79, 0x1a, 0x5d, 0x2d, 0x2a, 0x73, 0xcd, + 0x2a, 0x41, 0x5a, 0x8f, 0xf1, 0x80, 0x71, 0xe8, 0x20, 0x8e, 0xe1, 0xa0, 0xe5, 0x60, 0x81, 0x5a, + 0xb0, 0xc7, 0x48, 0x1a, 0x37, 0x10, 0x50, 0x3b, 0xdc, 0x7b, 0xcd, 0x3c, 0xcf, 0xc7, 0xfb, 0x93, + 0x5c, 0x75, 0x19, 0x54, 0x38, 0xa6, 0x2e, 0x8e, 0x6a, 0x4a, 0x43, 0x69, 0xce, 0xd9, 0xe9, 0x4a, + 0x5d, 0x07, 0x15, 0x4c, 0x91, 0xe3, 0xe3, 0xda, 0x54, 0x43, 0x69, 0xce, 0x5a, 0xd5, 0xab, 0xb1, + 0xfe, 0xff, 0x08, 0x05, 0xfe, 0xb6, 0x21, 0xf7, 0x0d, 0x3b, 0x05, 0x6c, 0xcf, 0x7e, 0x3c, 0xd5, + 0x4b, 0xbf, 0x4f, 0xf5, 0x92, 0xf1, 0xa5, 0x0c, 0x96, 0x3b, 0xdc, 0xdb, 0x73, 0x89, 0xc8, 0x2a, + 0x74, 0x51, 0x84, 0x02, 0x7e, 0x6b, 0x9d, 0x0d, 0x50, 0xcd, 0x1a, 0x39, 0x94, 0x84, 0xae, 0x2c, + 0x69, 0x2f, 0x66, 0x81, 0x3d, 0xb9, 0xaf, 0xbe, 0x03, 0x6a, 0xc8, 0xfc, 0x11, 0x65, 0x01, 0x41, + 0xfe, 0xe1, 0x11, 0xea, 0x09, 0x16, 0xf1, 0xda, 0x74, 0x63, 0xba, 0x39, 0x67, 0x99, 0x67, 0x63, + 0x5d, 0xf9, 0x31, 0xd6, 0x9f, 0x79, 0x44, 0x1c, 0xf7, 0x1d, 0xb3, 0xc7, 0x02, 0x98, 0x9e, 0x88, + 0x7c, 0x6d, 0x72, 0xf7, 0x3d, 0x14, 0xa3, 0x10, 0x73, 0x73, 0x17, 0xf7, 0xec, 0xea, 0x35, 0xd3, + 0x0b, 0x49, 0xa4, 0x7a, 0x60, 0xf9, 0x5a, 0x8b, 0x4b, 0xb8, 0x88, 0x88, 0xd3, 0x8f, 0x17, 0xb5, + 0x72, 0x43, 0x69, 0xce, 0xb7, 0x9f, 0x9b, 0x05, 0x3f, 0xd4, 0xcc, 0x3a, 0xdd, 0xcd, 0x65, 0x58, + 0xe5, 0x58, 0x8e, 0xfd, 0x88, 0x14, 0x05, 0xd5, 0x03, 0x50, 0xc5, 0x21, 0xeb, 0x1d, 0xf3, 0xc3, + 0x10, 0x47, 0xf1, 0x43, 0x98, 0x5b, 0xfb, 0x2f, 0x3e, 0x97, 0x3b, 0xb5, 0xb1, 0x4f, 0x85, 0xbd, + 0x20, 0x89, 0xba, 0x38, 0xea, 0x26, 0x34, 0xea, 0x1b, 0xb0, 0x28, 0x09, 0x25, 0xf9, 0x08, 0xa3, + 0xa8, 0x56, 0xb9, 0x17, 0xf5, 0x83, 0x94, 0xa7, 0x8b, 0xa3, 0xb7, 0x18, 0x45, 0x6a, 0x07, 0x80, + 0x00, 0x0d, 0x27, 0x72, 0x67, 0xee, 0xc5, 0x39, 0x17, 0xa0, 0xa1, 0x14, 0x9a, 0xb3, 0xcd, 0x0a, + 0xa8, 0xdf, 0x74, 0xa6, 0x8d, 0x79, 0xc8, 0x28, 0xc7, 0x46, 0x03, 0x68, 0xc5, 0x9e, 0xca, 0x10, + 0x43, 0x30, 0xd3, 0xe1, 0x9e, 0xd5, 0x8f, 0x68, 0x6c, 0xdb, 0xbc, 0xcd, 0xf2, 0xb6, 0x95, 0xfb, + 0x46, 0xe6, 0x3c, 0x0b, 0x94, 0xe3, 0xe9, 0x48, 0xcc, 0x36, 0xdf, 0x7e, 0x6c, 0x4a, 0xbd, 0x66, + 0x3c, 0x3e, 0x66, 0x3a, 0x3e, 0xe6, 0x0e, 0x23, 0xd4, 0x5a, 0x3a, 0x1b, 0xeb, 0xa5, 0xab, 0xb1, + 0x3e, 0x2f, 0x79, 0xe2, 0x24, 0xc3, 0x4e, 0x72, 0x8d, 0x2a, 0x58, 0x48, 0x2b, 0x4f, 0xc4, 0xb4, + 0xbf, 0x4e, 0x81, 0xe9, 0x0e, 0xf7, 0xd4, 0x13, 0x05, 0x2c, 0xfc, 0x3d, 0x6c, 0x6b, 0x85, 0x06, + 0xba, 0xd9, 0x7b, 0x1d, 0xfe, 0x23, 0x30, 0x3b, 0x82, 0xd5, 0x0f, 0xdf, 0x7e, 0x7d, 0x9a, 0x7a, + 0x6a, 0x3c, 0x81, 0x85, 0x37, 0x52, 0x92, 0xa5, 0x7e, 0x56, 0xc0, 0x52, 0xd1, 0x6c, 0x6e, 0xdc, + 0x56, 0xad, 0x00, 0x5c, 0xdf, 0xba, 0x03, 0x38, 0x93, 0x07, 0x13, 0x79, 0xeb, 0xc6, 0xda, 0x4d, + 0x79, 0xd8, 0x25, 0x62, 0x33, 0x5b, 0x6e, 0x86, 0x49, 0xa2, 0xf5, 0xf2, 0xec, 0x42, 0x53, 0xce, + 0x2f, 0x34, 0xe5, 0xe7, 0x85, 0xa6, 0x9c, 0x5c, 0x6a, 0xa5, 0xf3, 0x4b, 0xad, 0xf4, 0xfd, 0x52, + 0x2b, 0x1d, 0xb4, 0x73, 0x4e, 0x7b, 0x95, 0x90, 0xed, 0x1c, 0x23, 0x42, 0x27, 0xc4, 0x83, 0x36, + 0x1c, 0xe6, 0xd8, 0x13, 0xe7, 0x39, 0x95, 0xe4, 0x06, 0xdc, 0xfa, 0x13, 0x00, 0x00, 0xff, 0xff, + 0xbc, 0xc6, 0x23, 0xe2, 0xaf, 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -544,6 +639,69 @@ func (m *MsgEditInflationParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, return len(dAtA) - i, nil } +func (m *MsgBurn) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgBurn) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgBurn) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Coin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgBurnResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgBurnResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgBurnResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -627,6 +785,30 @@ func (m *MsgEditInflationParamsResponse) Size() (n int) { return n } +func (m *MsgBurn) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Coin.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgBurnResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1117,6 +1299,171 @@ func (m *MsgEditInflationParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgBurn) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgBurn: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgBurn: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Coin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgBurnResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgBurnResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgBurnResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/oracle/abci.go b/x/oracle/abci.go index be80ea9ac..fb7b62481 100644 --- a/x/oracle/abci.go +++ b/x/oracle/abci.go @@ -3,8 +3,8 @@ package oracle import ( "time" - "github.com/NibiruChain/nibiru/x/oracle/keeper" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/oracle/abci_test.go b/x/oracle/abci_test.go index 23a5e7ae4..2db3a55df 100644 --- a/x/oracle/abci_test.go +++ b/x/oracle/abci_test.go @@ -3,13 +3,13 @@ package oracle import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/keeper" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestOracleTallyTiming(t *testing.T) { @@ -18,7 +18,7 @@ func TestOracleTallyTiming(t *testing.T) { // all the Addrs vote for the block ... not last period block yet, so tally fails for i := range keeper.Addrs[:4] { keeper.MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{ - {Pair: asset.Registry.Pair(denoms.BTC, denoms.USD), ExchangeRate: sdk.OneDec()}, + {Pair: asset.Registry.Pair(denoms.BTC, denoms.USD), ExchangeRate: math.LegacyOneDec()}, }, i) } @@ -51,8 +51,8 @@ func TestOraclePriceExpiration(t *testing.T) { // Set prices for both pairs for i := range keeper.Addrs[:4] { keeper.MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{ - {Pair: pair1, ExchangeRate: sdk.OneDec()}, - {Pair: pair2, ExchangeRate: sdk.OneDec()}, + {Pair: pair1, ExchangeRate: math.LegacyOneDec()}, + {Pair: pair2, ExchangeRate: math.LegacyOneDec()}, }, i) } @@ -76,7 +76,7 @@ func TestOraclePriceExpiration(t *testing.T) { // Set prices for pair 1 for i := range keeper.Addrs[:4] { keeper.MakeAggregatePrevoteAndVote(t, input, h, 19, types.ExchangeRateTuples{ - {Pair: pair1, ExchangeRate: sdk.NewDec(2)}, + {Pair: pair1, ExchangeRate: math.LegacyNewDec(2)}, }, i) } diff --git a/x/oracle/client/cli/gen_pricefeeder_delegation.go b/x/oracle/cli/gen_pricefeeder_delegation.go similarity index 98% rename from x/oracle/client/cli/gen_pricefeeder_delegation.go rename to x/oracle/cli/gen_pricefeeder_delegation.go index 1dd5d599d..fe95ffcd1 100644 --- a/x/oracle/client/cli/gen_pricefeeder_delegation.go +++ b/x/oracle/cli/gen_pricefeeder_delegation.go @@ -14,7 +14,7 @@ import ( genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) const ( diff --git a/x/oracle/client/cli/gen_pricefeeder_delegation_test.go b/x/oracle/cli/gen_pricefeeder_delegation_test.go similarity index 86% rename from x/oracle/client/cli/gen_pricefeeder_delegation_test.go rename to x/oracle/cli/gen_pricefeeder_delegation_test.go index b0870e92a..cd1aa881c 100644 --- a/x/oracle/client/cli/gen_pricefeeder_delegation_test.go +++ b/x/oracle/cli/gen_pricefeeder_delegation_test.go @@ -4,17 +4,17 @@ import ( "fmt" "testing" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/oracle/client/cli" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/oracle/cli" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/stretchr/testify/require" ) -// Tests "add-genesis-perp-market", a command that adds a market to genesis.json func TestAddGenesisPricefeederDelegation(t *testing.T) { - app.SetPrefixes(app.AccountAddressPrefix) + app.SetPrefixes(appconst.AccountAddressPrefix) tests := []struct { name string diff --git a/x/oracle/client/cli/query.go b/x/oracle/cli/query.go similarity index 98% rename from x/oracle/client/cli/query.go rename to x/oracle/cli/query.go index e41200c39..1cf6a8157 100644 --- a/x/oracle/client/cli/query.go +++ b/x/oracle/cli/query.go @@ -6,8 +6,8 @@ import ( "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" diff --git a/x/oracle/client/cli/tx.go b/x/oracle/cli/tx.go similarity index 99% rename from x/oracle/client/cli/tx.go rename to x/oracle/cli/tx.go index 3d685c183..50d26b944 100644 --- a/x/oracle/client/cli/tx.go +++ b/x/oracle/cli/tx.go @@ -5,11 +5,11 @@ import ( "strings" "time" - "github.com/NibiruChain/nibiru/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/asset" "github.com/pkg/errors" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" diff --git a/x/oracle/genesis.go b/x/oracle/genesis.go index a6635d1eb..3d6937e5e 100644 --- a/x/oracle/genesis.go +++ b/x/oracle/genesis.go @@ -7,9 +7,9 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/oracle/keeper" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) // InitGenesis initialize default parameters diff --git a/x/oracle/genesis_test.go b/x/oracle/genesis_test.go index 3aa932c30..13ccdbf7f 100644 --- a/x/oracle/genesis_test.go +++ b/x/oracle/genesis_test.go @@ -3,12 +3,13 @@ package oracle_test import ( "testing" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/oracle" - "github.com/NibiruChain/nibiru/x/oracle/keeper" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle" + "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestExportInitGenesis(t *testing.T) { @@ -16,9 +17,14 @@ func TestExportInitGenesis(t *testing.T) { input.OracleKeeper.Params.Set(input.Ctx, types.DefaultParams()) input.OracleKeeper.FeederDelegations.Insert(input.Ctx, keeper.ValAddrs[0], keeper.Addrs[1]) - input.OracleKeeper.ExchangeRates.Insert(input.Ctx, "pair1:pair2", types.DatedPrice{ExchangeRate: sdk.NewDec(123), CreatedBlock: 0}) + input.OracleKeeper.ExchangeRates.Insert(input.Ctx, "pair1:pair2", + types.ExchangeRateAtBlock{ + ExchangeRate: math.LegacyNewDec(123), + CreatedBlock: 0, + BlockTimestampMs: 0, + }) input.OracleKeeper.Prevotes.Insert(input.Ctx, keeper.ValAddrs[0], types.NewAggregateExchangeRatePrevote(types.AggregateVoteHash{123}, keeper.ValAddrs[0], uint64(2))) - input.OracleKeeper.Votes.Insert(input.Ctx, keeper.ValAddrs[0], types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "foo", ExchangeRate: sdk.NewDec(123)}}, keeper.ValAddrs[0])) + input.OracleKeeper.Votes.Insert(input.Ctx, keeper.ValAddrs[0], types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "foo", ExchangeRate: math.LegacyNewDec(123)}}, keeper.ValAddrs[0])) input.OracleKeeper.WhitelistedPairs.Insert(input.Ctx, "pair1:pair1") input.OracleKeeper.WhitelistedPairs.Insert(input.Ctx, "pair2:pair2") input.OracleKeeper.MissCounters.Insert(input.Ctx, keeper.ValAddrs[0], 10) @@ -109,7 +115,7 @@ func TestInitGenesis(t *testing.T) { ExchangeRateTuples: []types.ExchangeRateTuple{ { Pair: "nibi:usd", - ExchangeRate: sdk.NewDec(10), + ExchangeRate: math.LegacyNewDec(10), }, }, Voter: "invalid", @@ -125,7 +131,7 @@ func TestInitGenesis(t *testing.T) { ExchangeRateTuples: []types.ExchangeRateTuple{ { Pair: "nibi:usd", - ExchangeRate: sdk.NewDec(10), + ExchangeRate: math.LegacyNewDec(10), }, }, Voter: keeper.ValAddrs[0].String(), diff --git a/x/oracle/integration/action/price.go b/x/oracle/integration/action/price.go deleted file mode 100644 index ef2741cb6..000000000 --- a/x/oracle/integration/action/price.go +++ /dev/null @@ -1,56 +0,0 @@ -package action - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/NibiruChain/collections" - - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/testutil/action" - "github.com/NibiruChain/nibiru/x/oracle/types" -) - -func SetOraclePrice(pair asset.Pair, price sdk.Dec) action.Action { - return &setPairPrice{ - Pair: pair, - Price: price, - } -} - -type setPairPrice struct { - Pair asset.Pair - Price sdk.Dec -} - -func (s setPairPrice) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { - app.OracleKeeper.SetPrice(ctx, s.Pair, s.Price) - - return ctx, nil, true -} - -func InsertOraclePriceSnapshot(pair asset.Pair, time time.Time, price sdk.Dec) action.Action { - return &insertOraclePriceSnapshot{ - Pair: pair, - Time: time, - Price: price, - } -} - -type insertOraclePriceSnapshot struct { - Pair asset.Pair - Time time.Time - Price sdk.Dec -} - -func (s insertOraclePriceSnapshot) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) { - app.OracleKeeper.PriceSnapshots.Insert(ctx, collections.Join(s.Pair, s.Time), types.PriceSnapshot{ - Pair: s.Pair, - Price: s.Price, - TimestampMs: s.Time.UnixMilli(), - }) - - return ctx, nil, true -} diff --git a/x/oracle/integration/app_test.go b/x/oracle/keeper/app_test.go similarity index 59% rename from x/oracle/integration/app_test.go rename to x/oracle/keeper/app_test.go index 80757a506..c8b5ebe33 100644 --- a/x/oracle/integration/app_test.go +++ b/x/oracle/keeper/app_test.go @@ -1,4 +1,4 @@ -package integration_test +package keeper_test import ( "context" @@ -9,32 +9,36 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/testutil" - testutilcli "github.com/NibiruChain/nibiru/x/common/testutil/cli" - "github.com/NibiruChain/nibiru/x/common/testutil/genesis" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/oracle/types" + "cosmossdk.io/math" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) -type IntegrationTestSuite struct { +var _ suite.TearDownAllSuite = (*TestSuite)(nil) + +type TestSuite struct { suite.Suite - cfg testutilcli.Config - network *testutilcli.Network + cfg testnetwork.Config + network *testnetwork.Network } -func (s *IntegrationTestSuite) SetupSuite() { +func (s *TestSuite) SetupSuite() { testutil.BeforeIntegrationSuite(s.T()) } -func (s *IntegrationTestSuite) SetupTest() { +func (s *TestSuite) SetupTest() { testapp.EnsureNibiruPrefix() homeDir := s.T().TempDir() genesisState := genesis.NewTestGenesisState(app.MakeEncodingConfig()) - s.cfg = testutilcli.BuildNetworkConfig(genesisState) + s.cfg = testnetwork.BuildNetworkConfig(genesisState) s.cfg.NumValidators = 4 s.cfg.GenesisState[types.ModuleName] = s.cfg.Codec.MustMarshalJSON(func() codec.ProtoMarshaler { gs := types.DefaultGenesisState() @@ -46,7 +50,7 @@ func (s *IntegrationTestSuite) SetupTest() { return gs }()) - network, err := testutilcli.New( + network, err := testnetwork.New( s.T(), homeDir, s.cfg, @@ -54,11 +58,10 @@ func (s *IntegrationTestSuite) SetupTest() { s.Require().NoError(err) s.network = network - _, err = s.network.WaitForHeight(2) - require.NoError(s.T(), err) + s.Require().NoError(s.network.WaitForNextBlock()) } -func (s *IntegrationTestSuite) TestSuccessfulVoting() { +func (s *TestSuite) TestSuccessfulVoting() { // assuming validators have equal power // we use the weighted median. // what happens is that prices are ordered @@ -69,20 +72,20 @@ func (s *IntegrationTestSuite) TestSuccessfulVoting() { // then the number picked is the one in the middle always. prices := []map[asset.Pair]sdk.Dec{ { - "nibi:usdc": sdk.OneDec(), - "btc:usdc": sdk.MustNewDecFromStr("100203.0"), + "nibi:usdc": math.LegacyOneDec(), + "btc:usdc": math.LegacyMustNewDecFromStr("100203.0"), }, { - "nibi:usdc": sdk.OneDec(), - "btc:usdc": sdk.MustNewDecFromStr("100150.5"), + "nibi:usdc": math.LegacyOneDec(), + "btc:usdc": math.LegacyMustNewDecFromStr("100150.5"), }, { - "nibi:usdc": sdk.OneDec(), - "btc:usdc": sdk.MustNewDecFromStr("100200.9"), + "nibi:usdc": math.LegacyOneDec(), + "btc:usdc": math.LegacyMustNewDecFromStr("100200.9"), }, { - "nibi:usdc": sdk.OneDec(), - "btc:usdc": sdk.MustNewDecFromStr("100300.9"), + "nibi:usdc": math.LegacyOneDec(), + "btc:usdc": math.LegacyMustNewDecFromStr("100300.9"), }, } votes := s.sendPrevotes(prices) @@ -96,14 +99,14 @@ func (s *IntegrationTestSuite) TestSuccessfulVoting() { gotPrices := s.currentPrices() require.Equal(s.T(), map[asset.Pair]sdk.Dec{ - "nibi:usdc": sdk.OneDec(), - "btc:usdc": sdk.MustNewDecFromStr("100200.9"), + "nibi:usdc": math.LegacyOneDec(), + "btc:usdc": math.LegacyMustNewDecFromStr("100200.9"), }, gotPrices, ) } -func (s *IntegrationTestSuite) sendPrevotes(prices []map[asset.Pair]sdk.Dec) []string { +func (s *TestSuite) sendPrevotes(prices []map[asset.Pair]sdk.Dec) []string { strVotes := make([]string, len(prices)) for i, val := range s.network.Validators { raw := prices[i] @@ -113,13 +116,14 @@ func (s *IntegrationTestSuite) sendPrevotes(prices []map[asset.Pair]sdk.Dec) []s } pricesStr, err := votes.ToString() - require.NoError(s.T(), err) - _, err = s.network.BroadcastMsgs(val.Address, &types.MsgAggregateExchangeRatePrevote{ + s.Require().NoError(err) + _, err = s.network.BroadcastMsgs(val.Address, nil, &types.MsgAggregateExchangeRatePrevote{ Hash: types.GetAggregateVoteHash("1", pricesStr, val.ValAddress).String(), Feeder: val.Address.String(), Validator: val.ValAddress.String(), }) - require.NoError(s.T(), err) + s.Require().NoError(err) + s.NoError(s.network.WaitForNextBlock()) strVotes[i] = pricesStr } @@ -127,19 +131,19 @@ func (s *IntegrationTestSuite) sendPrevotes(prices []map[asset.Pair]sdk.Dec) []s return strVotes } -func (s *IntegrationTestSuite) sendVotes(rates []string) { +func (s *TestSuite) sendVotes(rates []string) { for i, val := range s.network.Validators { - _, err := s.network.BroadcastMsgs(val.Address, &types.MsgAggregateExchangeRateVote{ + _, err := s.network.BroadcastMsgs(val.Address, nil, &types.MsgAggregateExchangeRateVote{ Salt: "1", ExchangeRates: rates[i], Feeder: val.Address.String(), Validator: val.ValAddress.String(), }) - require.NoError(s.T(), err) + s.Require().NoError(err) } } -func (s *IntegrationTestSuite) waitVoteRevealBlock() { +func (s *TestSuite) waitVoteRevealBlock() { params, err := types.NewQueryClient(s.network.Validators[0].ClientCtx).Params(context.Background(), &types.QueryParamsRequest{}) require.NoError(s.T(), err) @@ -155,11 +159,11 @@ func (s *IntegrationTestSuite) waitVoteRevealBlock() { } // it's an alias, but it exists to give better understanding of what we're doing in test cases scenarios -func (s *IntegrationTestSuite) waitPriceUpdateBlock() { +func (s *TestSuite) waitPriceUpdateBlock() { s.waitVoteRevealBlock() } -func (s *IntegrationTestSuite) currentPrices() map[asset.Pair]sdk.Dec { +func (s *TestSuite) currentPrices() map[asset.Pair]sdk.Dec { rawRates, err := types.NewQueryClient(s.network.Validators[0].ClientCtx).ExchangeRates(context.Background(), &types.QueryExchangeRatesRequest{}) require.NoError(s.T(), err) @@ -173,5 +177,12 @@ func (s *IntegrationTestSuite) currentPrices() map[asset.Pair]sdk.Dec { } func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) + testutil.RetrySuiteRunIfDbClosed(t, func() { + suite.Run(t, new(TestSuite)) + }, 2) +} + +func (s *TestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() } diff --git a/x/oracle/keeper/ballot.go b/x/oracle/keeper/ballot.go index 7af68d545..942c8b1dd 100644 --- a/x/oracle/keeper/ballot.go +++ b/x/oracle/keeper/ballot.go @@ -7,10 +7,12 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/omap" - "github.com/NibiruChain/nibiru/x/common/set" - "github.com/NibiruChain/nibiru/x/oracle/types" + "cosmossdk.io/math" + + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/omap" + "github.com/NibiruChain/nibiru/v2/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) // groupVotesByPair takes a collection of votes and groups them by their @@ -83,7 +85,7 @@ func (k Keeper) clearVotesAndPrevotes(ctx sdk.Context, votePeriod uint64) { func isPassingVoteThreshold( votes types.ExchangeRateVotes, thresholdVotingPower sdkmath.Int, minVoters uint64, ) bool { - totalPower := sdk.NewInt(votes.Power()) + totalPower := math.NewInt(votes.Power()) if totalPower.IsZero() { return false } @@ -116,7 +118,7 @@ func (k Keeper) removeInvalidVotes( ) // Iterate through sorted keys for deterministic ordering. - orderedPairVotes := omap.OrderedMap_Pair[types.ExchangeRateVotes](pairVotes) + orderedPairVotes := omap.SortedMap_Pair[types.ExchangeRateVotes](pairVotes) for pair := range orderedPairVotes.Range() { // If pair is not whitelisted, or the votes for it has failed, then skip // and remove it from pairBallotsMap for iteration efficiency @@ -157,6 +159,7 @@ func Tally( rewardSpread = standardDeviation } + missedValidators := make(map[string]bool) for _, v := range votes { // Filter votes winners & abstain voters isInsideSpread := v.ExchangeRate.GTE(weightedMedian.Sub(rewardSpread)) && @@ -171,7 +174,11 @@ func Tally( validatorPerformance.RewardWeight += v.Power validatorPerformance.WinCount++ case isMiss: - validatorPerformance.MissCount++ + // Only count a miss once per validator + if !missedValidators[v.Voter.String()] { + validatorPerformance.MissCount++ + missedValidators[v.Voter.String()] = true + } case isAbstainVote: validatorPerformance.AbstainCount++ } diff --git a/x/oracle/keeper/ballot_test.go b/x/oracle/keeper/ballot_test.go index b66e78033..46dee0c53 100644 --- a/x/oracle/keeper/ballot_test.go +++ b/x/oracle/keeper/ballot_test.go @@ -6,6 +6,7 @@ import ( stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "cosmossdk.io/math" "github.com/NibiruChain/collections" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" sdk "github.com/cosmos/cosmos-sdk/types" @@ -14,11 +15,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/set" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestGroupVotesByPair(t *testing.T) { @@ -40,14 +41,14 @@ func TestGroupVotesByPair(t *testing.T) { pairBtc := asset.Registry.Pair(denoms.BTC, denoms.NUSD) pairEth := asset.Registry.Pair(denoms.ETH, denoms.NUSD) btcVotes := types.ExchangeRateVotes{ - {Pair: pairBtc, ExchangeRate: sdk.NewDec(17), Voter: ValAddrs[0], Power: power}, - {Pair: pairBtc, ExchangeRate: sdk.NewDec(10), Voter: ValAddrs[1], Power: power}, - {Pair: pairBtc, ExchangeRate: sdk.NewDec(6), Voter: ValAddrs[2], Power: power}, + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(17), Voter: ValAddrs[0], Power: power}, + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(10), Voter: ValAddrs[1], Power: power}, + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(6), Voter: ValAddrs[2], Power: power}, } ethVotes := types.ExchangeRateVotes{ - {Pair: pairEth, ExchangeRate: sdk.NewDec(1_000), Voter: ValAddrs[0], Power: power}, - {Pair: pairEth, ExchangeRate: sdk.NewDec(1_300), Voter: ValAddrs[1], Power: power}, - {Pair: pairEth, ExchangeRate: sdk.NewDec(2_000), Voter: ValAddrs[2], Power: power}, + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(1_000), Voter: ValAddrs[0], Power: power}, + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(1_300), Voter: ValAddrs[1], Power: power}, + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(2_000), Voter: ValAddrs[2], Power: power}, } for i, v := range btcVotes { @@ -110,14 +111,14 @@ func TestClearVotesAndPrevotes(t *testing.T) { staking.EndBlocker(fixture.Ctx, &fixture.StakingKeeper) btcVotes := types.ExchangeRateVotes{ - types.NewExchangeRateVote(sdk.NewDec(17), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[0], power), - types.NewExchangeRateVote(sdk.NewDec(10), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[1], power), - types.NewExchangeRateVote(sdk.NewDec(6), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[2], power), + types.NewExchangeRateVote(math.LegacyNewDec(17), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[0], power), + types.NewExchangeRateVote(math.LegacyNewDec(10), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[1], power), + types.NewExchangeRateVote(math.LegacyNewDec(6), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[2], power), } ethVotes := types.ExchangeRateVotes{ - types.NewExchangeRateVote(sdk.NewDec(1000), asset.Registry.Pair(denoms.ETH, denoms.NUSD), ValAddrs[0], power), - types.NewExchangeRateVote(sdk.NewDec(1300), asset.Registry.Pair(denoms.ETH, denoms.NUSD), ValAddrs[1], power), - types.NewExchangeRateVote(sdk.NewDec(2000), asset.Registry.Pair(denoms.ETH, denoms.NUSD), ValAddrs[2], power), + types.NewExchangeRateVote(math.LegacyNewDec(1000), asset.Registry.Pair(denoms.ETH, denoms.NUSD), ValAddrs[0], power), + types.NewExchangeRateVote(math.LegacyNewDec(1300), asset.Registry.Pair(denoms.ETH, denoms.NUSD), ValAddrs[1], power), + types.NewExchangeRateVote(math.LegacyNewDec(2000), asset.Registry.Pair(denoms.ETH, denoms.NUSD), ValAddrs[2], power), } for i := range btcVotes { @@ -153,7 +154,7 @@ func TestFuzzTally(t *testing.T) { f := fuzz.New().NilChance(0).Funcs( func(e *sdk.Dec, c fuzz.Continue) { - *e = sdk.NewDec(c.Int63()) + *e = math.LegacyNewDec(c.Int63()) }, func(e *map[string]int64, c fuzz.Continue) { numValidators := c.Intn(100) + 5 @@ -201,6 +202,43 @@ func TestFuzzTally(t *testing.T) { }) } +func TestTallyMissCount(t *testing.T) { + fixture := CreateTestFixture(t) + + power := int64(100) + amt := sdk.TokensFromConsensusPower(power, sdk.DefaultPowerReduction) + sh := stakingkeeper.NewMsgServerImpl(&fixture.StakingKeeper) + + // Validator created + _, err := sh.CreateValidator(fixture.Ctx, NewTestMsgCreateValidator(ValAddrs[0], ValPubKeys[0], amt)) + require.NoError(t, err) + _, err = sh.CreateValidator(fixture.Ctx, NewTestMsgCreateValidator(ValAddrs[1], ValPubKeys[1], amt)) + require.NoError(t, err) + _, err = sh.CreateValidator(fixture.Ctx, NewTestMsgCreateValidator(ValAddrs[2], ValPubKeys[2], amt)) + require.NoError(t, err) + staking.EndBlocker(fixture.Ctx, &fixture.StakingKeeper) + + pairBtc := asset.Registry.Pair(denoms.BTC, denoms.NUSD) + pairEth := asset.Registry.Pair(denoms.ETH, denoms.NUSD) + // Having validator2 to have 2 votes outside the spread + btcVotes := types.ExchangeRateVotes{ + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(17), Voter: ValAddrs[0], Power: power}, + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(10), Voter: ValAddrs[1], Power: power}, + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(20000), Voter: ValAddrs[2], Power: power}, + } + ethVotes := types.ExchangeRateVotes{ + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(1_000), Voter: ValAddrs[0], Power: power}, + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(1_300), Voter: ValAddrs[1], Power: power}, + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(1), Voter: ValAddrs[2], Power: power}, + } + + allVotes := append(btcVotes, ethVotes...) + + claimMap := types.ValidatorPerformances{} + Tally(allVotes, math.LegacyNewDec(10), claimMap) + assert.Equal(t, int64(1), claimMap[ValAddrs[2].String()].MissCount) +} + type VoteMap = map[asset.Pair]types.ExchangeRateVotes func TestRemoveInvalidBallots(t *testing.T) { @@ -240,7 +278,7 @@ func TestRemoveInvalidBallots(t *testing.T) { name: "empty key, nonempty votes, not whitelisted", voteMap: VoteMap{ "": types.ExchangeRateVotes{ - {Pair: "", ExchangeRate: sdk.ZeroDec(), Voter: sdk.ValAddress{}, Power: 0}, + {Pair: "", ExchangeRate: math.LegacyZeroDec(), Voter: sdk.ValAddress{}, Power: 0}, }, }, }, @@ -289,7 +327,7 @@ func TestFuzzPickReferencePair(t *testing.T) { } }, func(e *sdk.Dec, c fuzz.Continue) { - *e = sdk.NewDec(c.Int63()) + *e = math.LegacyNewDec(c.Int63()) }, func(e *map[asset.Pair]sdk.Dec, c fuzz.Continue) { for _, pair := range pairs { @@ -350,10 +388,10 @@ func TestFuzzPickReferencePair(t *testing.T) { func TestZeroBallotPower(t *testing.T) { btcVotess := types.ExchangeRateVotes{ - types.NewExchangeRateVote(sdk.NewDec(17), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[0], 0), - types.NewExchangeRateVote(sdk.NewDec(10), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[1], 0), - types.NewExchangeRateVote(sdk.NewDec(6), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[2], 0), + types.NewExchangeRateVote(math.LegacyNewDec(17), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[0], 0), + types.NewExchangeRateVote(math.LegacyNewDec(10), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[1], 0), + types.NewExchangeRateVote(math.LegacyNewDec(6), asset.Registry.Pair(denoms.BTC, denoms.NUSD), ValAddrs[2], 0), } - assert.False(t, isPassingVoteThreshold(btcVotess, sdk.ZeroInt(), 0)) + assert.False(t, isPassingVoteThreshold(btcVotess, math.ZeroInt(), 0)) } diff --git a/x/oracle/keeper/edit_params_test.go b/x/oracle/keeper/edit_params_test.go index d197a97cc..2e2bf702a 100644 --- a/x/oracle/keeper/edit_params_test.go +++ b/x/oracle/keeper/edit_params_test.go @@ -6,11 +6,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/oracle/keeper" - "github.com/NibiruChain/nibiru/x/oracle/types" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) func TestMsgServer_EditOracleParams(t *testing.T) { diff --git a/x/oracle/keeper/querier.go b/x/oracle/keeper/grpc_query.go similarity index 95% rename from x/oracle/keeper/querier.go rename to x/oracle/keeper/grpc_query.go index 7e298c6d4..4410fdc7f 100644 --- a/x/oracle/keeper/querier.go +++ b/x/oracle/keeper/grpc_query.go @@ -9,8 +9,8 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) // querier is used as Keeper will have duplicate methods if used directly, and gRPC names take precedence over q @@ -50,12 +50,15 @@ func (q querier) ExchangeRate(c context.Context, req *types.QueryExchangeRateReq } ctx := sdk.UnwrapSDKContext(c) - exchangeRate, err := q.Keeper.GetExchangeRate(ctx, req.Pair) + out, err := q.Keeper.ExchangeRates.Get(ctx, req.Pair) if err != nil { return nil, err } - - return &types.QueryExchangeRateResponse{ExchangeRate: exchangeRate}, nil + return &types.QueryExchangeRateResponse{ + ExchangeRate: out.ExchangeRate, + BlockTimestampMs: out.BlockTimestampMs, + BlockHeight: out.CreatedBlock, + }, nil } /* diff --git a/x/oracle/keeper/querier_test.go b/x/oracle/keeper/grpc_query_test.go similarity index 64% rename from x/oracle/keeper/querier_test.go rename to x/oracle/keeper/grpc_query_test.go index de20ca86b..c6ff2f4ad 100644 --- a/x/oracle/keeper/querier_test.go +++ b/x/oracle/keeper/grpc_query_test.go @@ -5,15 +5,17 @@ import ( "testing" "time" + "cosmossdk.io/math" "github.com/NibiruChain/collections" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - testutilevents "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/set" + testutilevents "github.com/NibiruChain/nibiru/v2/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestQueryParams(t *testing.T) { @@ -35,8 +37,16 @@ func TestQueryExchangeRate(t *testing.T) { ctx := sdk.WrapSDKContext(input.Ctx) querier := NewQuerier(input.OracleKeeper) - rate := sdk.NewDec(1700) - input.OracleKeeper.ExchangeRates.Insert(input.Ctx, asset.Registry.Pair(denoms.ETH, denoms.NUSD), types.DatedPrice{ExchangeRate: rate, CreatedBlock: uint64(input.Ctx.BlockHeight())}) + rate := math.LegacyNewDec(1700) + input.OracleKeeper.ExchangeRates.Insert( + input.Ctx, + asset.Registry.Pair(denoms.ETH, denoms.NUSD), + types.ExchangeRateAtBlock{ + ExchangeRate: rate, + CreatedBlock: uint64(input.Ctx.BlockHeight()), + BlockTimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) // empty request _, err := querier.ExchangeRate(ctx, nil) @@ -75,9 +85,25 @@ func TestQueryExchangeRates(t *testing.T) { ctx := sdk.WrapSDKContext(input.Ctx) querier := NewQuerier(input.OracleKeeper) - rate := sdk.NewDec(1700) - input.OracleKeeper.ExchangeRates.Insert(input.Ctx, asset.Registry.Pair(denoms.BTC, denoms.NUSD), types.DatedPrice{ExchangeRate: rate, CreatedBlock: uint64(input.Ctx.BlockHeight())}) - input.OracleKeeper.ExchangeRates.Insert(input.Ctx, asset.Registry.Pair(denoms.ETH, denoms.NUSD), types.DatedPrice{ExchangeRate: rate, CreatedBlock: uint64(input.Ctx.BlockHeight())}) + rate := math.LegacyNewDec(1700) + input.OracleKeeper.ExchangeRates.Insert( + input.Ctx, + asset.Registry.Pair(denoms.BTC, denoms.NUSD), + types.ExchangeRateAtBlock{ + ExchangeRate: rate, + CreatedBlock: uint64(input.Ctx.BlockHeight()), + BlockTimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + input.OracleKeeper.ExchangeRates.Insert( + input.Ctx, + asset.Registry.Pair(denoms.ETH, denoms.NUSD), + types.ExchangeRateAtBlock{ + ExchangeRate: rate, + CreatedBlock: uint64(input.Ctx.BlockHeight()), + BlockTimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) res, err := querier.ExchangeRates(ctx, &types.QueryExchangeRatesRequest{}) require.NoError(t, err) @@ -92,7 +118,7 @@ func TestQueryExchangeRateTwap(t *testing.T) { input := CreateTestFixture(t) querier := NewQuerier(input.OracleKeeper) - rate := sdk.NewDec(1700) + rate := math.LegacyNewDec(1700) input.OracleKeeper.SetPrice(input.Ctx, asset.Registry.Pair(denoms.BTC, denoms.NUSD), rate) testutilevents.RequireContainsTypedEvent( t, @@ -114,7 +140,133 @@ func TestQueryExchangeRateTwap(t *testing.T) { res, err := querier.ExchangeRateTwap(ctx, &types.QueryExchangeRateRequest{Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD)}) require.NoError(t, err) - require.Equal(t, sdk.MustNewDecFromStr("1700"), res.ExchangeRate) + require.Equal(t, math.LegacyMustNewDecFromStr("1700"), res.ExchangeRate) +} + +func TestQueryDatedExchangeRate(t *testing.T) { + input := CreateTestFixture(t) + querier := NewQuerier(input.OracleKeeper) + + initialTime := input.Ctx.BlockTime() + initialHeight := input.Ctx.BlockHeight() + t.Logf("Set initial block time and height\ninitialTimeSeconds: %d, intialHeight: %d", + initialTime.Unix(), + initialHeight, + ) + + // Pair 1: BTC/NUSD + pairBTC := asset.Registry.Pair(denoms.BTC, denoms.NUSD) + rateBTC1 := math.LegacyNewDec(1700) + rateBTC2 := math.LegacyNewDec(1800) + + // Pair 2: ETH/NUSD + pairETH := asset.Registry.Pair(denoms.ETH, denoms.NUSD) + rateETH1 := math.LegacyNewDec(100) + rateETH2 := math.LegacyNewDec(110) + + // --- Set first price for BTC/NUSD --- + input.OracleKeeper.SetPrice(input.Ctx, pairBTC, rateBTC1) + testutilevents.RequireContainsTypedEvent( + t, + input.Ctx, + &types.EventPriceUpdate{ + Pair: pairBTC.String(), + Price: rateBTC1, + TimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + + blockTime := initialTime.Add(1 * time.Second) + blockHeight := initialHeight + 1 + t.Logf("Advance time and block height\nblockTimeSeconds: %d, blockHeight: %d", + blockTime.Unix(), + blockHeight, + ) + input.Ctx = input.Ctx. + WithBlockTime(blockTime). + WithBlockHeight(blockHeight) + + t.Log("Set first price for ETH") + input.OracleKeeper.SetPrice(input.Ctx, pairETH, rateETH1) + testutilevents.RequireContainsTypedEvent( + t, + input.Ctx, + &types.EventPriceUpdate{ + Pair: pairETH.String(), + Price: rateETH1, + TimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + + blockTime = initialTime.Add(2 * time.Second) + blockHeight = initialHeight + 2 + t.Logf("Advance time and block height\nblockTimeSeconds: %d, blockHeight: %d", + blockTime.Unix(), + blockHeight, + ) + input.Ctx = input.Ctx. + WithBlockTime(blockTime). + WithBlockHeight(blockHeight) + + t.Log("Set second price for BTC") + input.OracleKeeper.SetPrice(input.Ctx, pairBTC, rateBTC2) + testutilevents.RequireContainsTypedEvent( + t, + input.Ctx, + &types.EventPriceUpdate{ + Pair: pairBTC.String(), + Price: rateBTC2, + TimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + + t.Log("Advance time and block height") + input.Ctx = input.Ctx. + WithBlockTime(initialTime.Add(3 * time.Second)). + WithBlockHeight(initialHeight + 3) + + t.Log("Set second price for ETH") + input.OracleKeeper.SetPrice(input.Ctx, pairETH, rateETH2) + testutilevents.RequireContainsTypedEvent( + t, + input.Ctx, + &types.EventPriceUpdate{ + Pair: pairETH.String(), + Price: rateETH2, + TimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + + // Wrap context for querying + ctx := sdk.WrapSDKContext(input.Ctx) + + t.Log("Query latest snapshot for BTC") + resBTC, err := querier.ExchangeRate( + ctx, + &types.QueryExchangeRateRequest{Pair: pairBTC}, + ) + require.NoError(t, err) + res := resBTC + require.Equal(t, rateBTC2.String(), resBTC.ExchangeRate.String()) + require.Equal(t, initialTime.Add(2*time.Second).UnixMilli(), res.BlockTimestampMs) + require.Equal(t, uint64(initialHeight+2), res.BlockHeight) + + t.Log("Query latest snapshot for ETH") + resETH, err := querier.ExchangeRate( + ctx, + &types.QueryExchangeRateRequest{Pair: pairETH}, + ) + require.NoError(t, err) + res = resETH + require.Equal(t, rateETH2, res.ExchangeRate) + require.Equal(t, initialTime.Add(3*time.Second).UnixMilli(), res.BlockTimestampMs) + require.Equal(t, uint64(initialHeight+3), res.BlockHeight) + + t.Run("Query a pair with no snapshots (should return an error)", func(t *testing.T) { + pairATOM := asset.Registry.Pair(denoms.ATOM, denoms.NUSD) + _, err = querier.ExchangeRate(ctx, &types.QueryExchangeRateRequest{Pair: pairATOM}) + require.Error(t, err) + }) } func TestCalcTwap(t *testing.T) { @@ -136,29 +288,29 @@ func TestCalcTwap(t *testing.T) { priceSnapshots: []types.PriceSnapshot{ { Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), - Price: sdk.MustNewDecFromStr("90000.0"), + Price: math.LegacyMustNewDecFromStr("90000.0"), TimestampMs: time.UnixMilli(1).UnixMilli(), }, { Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), - Price: sdk.MustNewDecFromStr("9.0"), + Price: math.LegacyMustNewDecFromStr("9.0"), TimestampMs: time.UnixMilli(10).UnixMilli(), }, { Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), - Price: sdk.MustNewDecFromStr("8.5"), + Price: math.LegacyMustNewDecFromStr("8.5"), TimestampMs: time.UnixMilli(20).UnixMilli(), }, { Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), - Price: sdk.MustNewDecFromStr("9.5"), + Price: math.LegacyMustNewDecFromStr("9.5"), TimestampMs: time.UnixMilli(30).UnixMilli(), }, }, currentBlockTime: time.UnixMilli(35), currentBlockHeight: 3, lookbackInterval: 30 * time.Millisecond, - expectedPrice: sdk.MustNewDecFromStr("8.900000000000000000"), + expectedPrice: math.LegacyMustNewDecFromStr("8.900000000000000000"), }, } @@ -205,21 +357,27 @@ func TestQueryActives(t *testing.T) { ctx := sdk.WrapSDKContext(input.Ctx) queryClient := NewQuerier(input.OracleKeeper) - rate := sdk.NewDec(1700) - input.OracleKeeper.ExchangeRates.Insert(input.Ctx, asset.Registry.Pair(denoms.BTC, denoms.NUSD), types.DatedPrice{ExchangeRate: rate, CreatedBlock: uint64(input.Ctx.BlockHeight())}) - input.OracleKeeper.ExchangeRates.Insert(input.Ctx, asset.Registry.Pair(denoms.NIBI, denoms.NUSD), types.DatedPrice{ExchangeRate: rate, CreatedBlock: uint64(input.Ctx.BlockHeight())}) - input.OracleKeeper.ExchangeRates.Insert(input.Ctx, asset.Registry.Pair(denoms.ETH, denoms.NUSD), types.DatedPrice{ExchangeRate: rate, CreatedBlock: uint64(input.Ctx.BlockHeight())}) + rate := math.LegacyNewDec(1700) + targetPairs := set.New[asset.Pair]() + for _, bankDenom := range []string{denoms.BTC, denoms.ETH, denoms.NIBI} { + pair := asset.Registry.Pair(bankDenom, denoms.NUSD) + targetPairs.Add(pair) + input.OracleKeeper.ExchangeRates.Insert( + input.Ctx, + pair, + types.ExchangeRateAtBlock{ + ExchangeRate: rate, + CreatedBlock: uint64(input.Ctx.BlockHeight()), + BlockTimestampMs: input.Ctx.BlockTime().UnixMilli(), + }, + ) + } res, err := queryClient.Actives(ctx, &types.QueryActivesRequest{}) require.NoError(t, err) - - targetPairs := []asset.Pair{ - asset.Registry.Pair(denoms.BTC, denoms.NUSD), - asset.Registry.Pair(denoms.ETH, denoms.NUSD), - asset.Registry.Pair(denoms.NIBI, denoms.NUSD), + for _, pair := range res.Actives { + require.True(t, targetPairs.Has(pair)) } - - require.Equal(t, targetPairs, res.Actives) } func TestQueryFeederDelegation(t *testing.T) { @@ -297,9 +455,9 @@ func TestQueryAggregateVote(t *testing.T) { ctx := sdk.WrapSDKContext(input.Ctx) querier := NewQuerier(input.OracleKeeper) - vote1 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: sdk.OneDec()}}, ValAddrs[0]) + vote1 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: math.LegacyOneDec()}}, ValAddrs[0]) input.OracleKeeper.Votes.Insert(input.Ctx, ValAddrs[0], vote1) - vote2 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: sdk.OneDec()}}, ValAddrs[1]) + vote2 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: math.LegacyOneDec()}}, ValAddrs[1]) input.OracleKeeper.Votes.Insert(input.Ctx, ValAddrs[1], vote2) // empty request @@ -326,11 +484,11 @@ func TestQueryAggregateVotes(t *testing.T) { ctx := sdk.WrapSDKContext(input.Ctx) querier := NewQuerier(input.OracleKeeper) - vote1 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: sdk.OneDec()}}, ValAddrs[0]) + vote1 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: math.LegacyOneDec()}}, ValAddrs[0]) input.OracleKeeper.Votes.Insert(input.Ctx, ValAddrs[0], vote1) - vote2 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: sdk.OneDec()}}, ValAddrs[1]) + vote2 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: math.LegacyOneDec()}}, ValAddrs[1]) input.OracleKeeper.Votes.Insert(input.Ctx, ValAddrs[1], vote2) - vote3 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: sdk.OneDec()}}, ValAddrs[2]) + vote3 := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{{Pair: "", ExchangeRate: math.LegacyOneDec()}}, ValAddrs[2]) input.OracleKeeper.Votes.Insert(input.Ctx, ValAddrs[2], vote3) expectedVotes := []types.AggregateExchangeRateVote{vote1, vote2, vote3} diff --git a/x/oracle/keeper/hooks.go b/x/oracle/keeper/hooks.go index 077361353..ec4c50fe0 100644 --- a/x/oracle/keeper/hooks.go +++ b/x/oracle/keeper/hooks.go @@ -5,8 +5,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/keeper" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - "github.com/NibiruChain/nibiru/x/epochs/types" - oracletypes "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/epochs/types" + oracletypes "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) var _ types.EpochHooks = Hooks{} diff --git a/x/oracle/keeper/keeper.go b/x/oracle/keeper/keeper.go index 6bc13bf8f..e7beb8026 100644 --- a/x/oracle/keeper/keeper.go +++ b/x/oracle/keeper/keeper.go @@ -7,6 +7,7 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdkerrors "cosmossdk.io/errors" + "cosmossdk.io/math" "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -14,8 +15,8 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) // Keeper of the oracle store @@ -28,30 +29,38 @@ type Keeper struct { distrKeeper types.DistributionKeeper StakingKeeper types.StakingKeeper slashingKeeper types.SlashingKeeper + sudoKeeper types.SudoKeeper distrModuleName string + // Module parameters Params collections.Item[types.Params] - ExchangeRates collections.Map[asset.Pair, types.DatedPrice] + ExchangeRates collections.Map[asset.Pair, types.ExchangeRateAtBlock] FeederDelegations collections.Map[sdk.ValAddress, sdk.AccAddress] MissCounters collections.Map[sdk.ValAddress, uint64] Prevotes collections.Map[sdk.ValAddress, types.AggregateExchangeRatePrevote] Votes collections.Map[sdk.ValAddress, types.AggregateExchangeRateVote] // PriceSnapshots maps types.PriceSnapshot to the asset.Pair of the snapshot and the creation timestamp as keys.Uint64Key. - PriceSnapshots collections.Map[collections.Pair[asset.Pair, time.Time], types.PriceSnapshot] + PriceSnapshots collections.Map[ + collections.Pair[asset.Pair, time.Time], + types.PriceSnapshot] WhitelistedPairs collections.KeySet[asset.Pair] Rewards collections.Map[uint64, types.Rewards] RewardsID collections.Sequence } // NewKeeper constructs a new keeper for oracle -func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, +func NewKeeper( + cdc codec.BinaryCodec, + storeKey storetypes.StoreKey, + accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, distrKeeper types.DistributionKeeper, stakingKeeper types.StakingKeeper, slashingKeeper types.SlashingKeeper, + sudoKeeper types.SudoKeeper, distrName string, ) Keeper { @@ -60,7 +69,7 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) } - return Keeper{ + k := Keeper{ cdc: cdc, storeKey: storeKey, AccountKeeper: accountKeeper, @@ -68,9 +77,10 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, distrKeeper: distrKeeper, StakingKeeper: stakingKeeper, slashingKeeper: slashingKeeper, + sudoKeeper: sudoKeeper, distrModuleName: distrName, Params: collections.NewItem(storeKey, 11, collections.ProtoValueEncoder[types.Params](cdc)), - ExchangeRates: collections.NewMap(storeKey, 1, asset.PairKeyEncoder, collections.ProtoValueEncoder[types.DatedPrice](cdc)), + ExchangeRates: collections.NewMap(storeKey, 1, asset.PairKeyEncoder, collections.ProtoValueEncoder[types.ExchangeRateAtBlock](cdc)), PriceSnapshots: collections.NewMap(storeKey, 10, collections.PairKeyEncoder(asset.PairKeyEncoder, collections.TimeKeyEncoder), collections.ProtoValueEncoder[types.PriceSnapshot](cdc)), FeederDelegations: collections.NewMap(storeKey, 2, collections.ValAddressKeyEncoder, collections.AccAddressValueEncoder), MissCounters: collections.NewMap(storeKey, 3, collections.ValAddressKeyEncoder, collections.Uint64ValueEncoder), @@ -82,6 +92,7 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, collections.Uint64KeyEncoder, collections.ProtoValueEncoder[types.Rewards](cdc)), RewardsID: collections.NewSequence(storeKey, 9), } + return k } // Logger returns a module-specific logger. @@ -119,7 +130,7 @@ func (k Keeper) ValidateFeeder( func (k Keeper) GetExchangeRateTwap(ctx sdk.Context, pair asset.Pair) (price sdk.Dec, err error) { params, err := k.Params.Get(ctx) if err != nil { - return sdk.OneDec().Neg(), err + return math.LegacyOneDec().Neg(), err } snapshots := k.PriceSnapshots.Iterate( @@ -134,7 +145,7 @@ func (k Keeper) GetExchangeRateTwap(ctx sdk.Context, pair asset.Pair) (price sdk if len(snapshots) == 0 { // if there are no snapshots, return -1 for the price - return sdk.OneDec().Neg(), types.ErrNoValidTWAP.Wrapf("no snapshots for pair %s", pair.String()) + return math.LegacyOneDec().Neg(), types.ErrNoValidTWAP.Wrapf("no snapshots for pair %s", pair.String()) } if len(snapshots) == 1 { @@ -144,7 +155,7 @@ func (k Keeper) GetExchangeRateTwap(ctx sdk.Context, pair asset.Pair) (price sdk firstTimestampMs := snapshots[0].TimestampMs if firstTimestampMs > ctx.BlockTime().UnixMilli() { // should never happen, or else we have corrupted state - return sdk.OneDec().Neg(), types.ErrNoValidTWAP.Wrapf( + return math.LegacyOneDec().Neg(), types.ErrNoValidTWAP.Wrapf( "Possible corrupted state. First timestamp %d is after current blocktime %d", firstTimestampMs, ctx.BlockTime().UnixMilli()) } @@ -153,11 +164,12 @@ func (k Keeper) GetExchangeRateTwap(ctx sdk.Context, pair asset.Pair) (price sdk return snapshots[0].Price, nil } - cumulativePrice := sdk.ZeroDec() + cumulativePrice := math.LegacyZeroDec() for i, s := range snapshots { var nextTimestampMs int64 if i == len(snapshots)-1 { - // if we're at the last snapshot, then consider that price as ongoing until the current blocktime + // if we're at the last snapshot, then consider that price as ongoing + // until the current blocktime nextTimestampMs = ctx.BlockTime().UnixMilli() } else { nextTimestampMs = snapshots[i+1].TimestampMs @@ -170,27 +182,26 @@ func (k Keeper) GetExchangeRateTwap(ctx sdk.Context, pair asset.Pair) (price sdk return cumulativePrice.QuoInt64(ctx.BlockTime().UnixMilli() - firstTimestampMs), nil } -func (k Keeper) GetExchangeRate(ctx sdk.Context, pair asset.Pair) (price sdk.Dec, err error) { - exchangeRate, err := k.ExchangeRates.Get(ctx, pair) - price = exchangeRate.ExchangeRate - return -} - // SetPrice sets the price for a pair as well as the price snapshot. func (k Keeper) SetPrice(ctx sdk.Context, pair asset.Pair, price sdk.Dec) { - k.ExchangeRates.Insert(ctx, pair, types.DatedPrice{ExchangeRate: price, CreatedBlock: uint64(ctx.BlockHeight())}) + blockTimestampMs := ctx.BlockTime().UnixMilli() + k.ExchangeRates.Insert(ctx, pair, + types.ExchangeRateAtBlock{ + ExchangeRate: price, + CreatedBlock: uint64(ctx.BlockHeight()), + BlockTimestampMs: blockTimestampMs, + }) key := collections.Join(pair, ctx.BlockTime()) - timestampMs := ctx.BlockTime().UnixMilli() k.PriceSnapshots.Insert(ctx, key, types.PriceSnapshot{ Pair: pair, Price: price, - TimestampMs: timestampMs, + TimestampMs: blockTimestampMs, }) if err := ctx.EventManager().EmitTypedEvent(&types.EventPriceUpdate{ Pair: pair.String(), Price: price, - TimestampMs: timestampMs, + TimestampMs: blockTimestampMs, }); err != nil { ctx.Logger().Error("failed to emit OraclePriceUpdate", "pair", pair, "error", err) } diff --git a/x/oracle/keeper/msg_server.go b/x/oracle/keeper/msg_server.go index 4f0cc3604..7e91bafaf 100644 --- a/x/oracle/keeper/msg_server.go +++ b/x/oracle/keeper/msg_server.go @@ -4,16 +4,14 @@ import ( "context" "fmt" - "github.com/cosmos/cosmos-sdk/types/errors" - - sudokeeper "github.com/NibiruChain/nibiru/x/sudo/keeper" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" - sdkerrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" + sudokeeper "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) type msgServer struct { @@ -118,7 +116,7 @@ func (ms msgServer) AggregateExchangeRateVote( hash := types.GetAggregateVoteHash(msg.Salt, msg.ExchangeRates, valAddr) if aggregatePrevote.Hash != hash.String() { return nil, sdkerrors.Wrapf( - types.ErrVerificationFailed, "must be given %s not %s", aggregatePrevote.Hash, hash, + types.ErrHashVerificationFailed, "must be given %s not %s", aggregatePrevote.Hash, hash, ) } @@ -173,6 +171,8 @@ func (ms msgServer) DelegateFeedConsent( return &types.MsgDelegateFeedConsentResponse{}, err } +// EditOracleParams: gRPC tx msg for editing the oracle module params. +// [SUDO] Only callable by sudoers. func (ms msgServer) EditOracleParams(goCtx context.Context, msg *types.MsgEditOracleParams) (*types.MsgEditOracleParamsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) diff --git a/x/oracle/keeper/msg_server_test.go b/x/oracle/keeper/msg_server_test.go index e3a0a7956..4b8aa0714 100644 --- a/x/oracle/keeper/msg_server_test.go +++ b/x/oracle/keeper/msg_server_test.go @@ -6,9 +6,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/types" + "cosmossdk.io/math" + + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestFeederDelegation(t *testing.T) { @@ -84,47 +86,47 @@ func TestAggregatePrevoteVote(t *testing.T) { exchangeRates := types.ExchangeRateTuples{ { Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), - ExchangeRate: sdk.MustNewDecFromStr("1000.23"), + ExchangeRate: math.LegacyMustNewDecFromStr("1000.23"), }, { Pair: asset.Registry.Pair(denoms.ETH, denoms.USD), - ExchangeRate: sdk.MustNewDecFromStr("0.29"), + ExchangeRate: math.LegacyMustNewDecFromStr("0.29"), }, { Pair: asset.Registry.Pair(denoms.BTC, denoms.USD), - ExchangeRate: sdk.MustNewDecFromStr("0.27"), + ExchangeRate: math.LegacyMustNewDecFromStr("0.27"), }, } otherExchangeRate := types.ExchangeRateTuples{ { Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), - ExchangeRate: sdk.MustNewDecFromStr("1000.23"), + ExchangeRate: math.LegacyMustNewDecFromStr("1000.23"), }, { Pair: asset.Registry.Pair(denoms.ETH, denoms.USD), - ExchangeRate: sdk.MustNewDecFromStr("0.29"), + ExchangeRate: math.LegacyMustNewDecFromStr("0.29"), }, { Pair: asset.Registry.Pair(denoms.ETH, denoms.USD), - ExchangeRate: sdk.MustNewDecFromStr("0.27"), + ExchangeRate: math.LegacyMustNewDecFromStr("0.27"), }, } unintendedExchangeRateStr := types.ExchangeRateTuples{ { Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), - ExchangeRate: sdk.MustNewDecFromStr("1000.23"), + ExchangeRate: math.LegacyMustNewDecFromStr("1000.23"), }, { Pair: asset.Registry.Pair(denoms.ETH, denoms.USD), - ExchangeRate: sdk.MustNewDecFromStr("0.29"), + ExchangeRate: math.LegacyMustNewDecFromStr("0.29"), }, { Pair: "BTC:CNY", - ExchangeRate: sdk.MustNewDecFromStr("0.27"), + ExchangeRate: math.LegacyMustNewDecFromStr("0.27"), }, } exchangeRatesStr, err := exchangeRates.ToString() diff --git a/x/oracle/keeper/params.go b/x/oracle/keeper/params.go index 13fdee2d5..5da6d0ead 100644 --- a/x/oracle/keeper/params.go +++ b/x/oracle/keeper/params.go @@ -1,8 +1,8 @@ package keeper import ( - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/x/oracle/keeper/params_test.go b/x/oracle/keeper/params_test.go index f01905af5..5b31887c2 100644 --- a/x/oracle/keeper/params_test.go +++ b/x/oracle/keeper/params_test.go @@ -4,12 +4,13 @@ import ( "testing" "time" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestParams(t *testing.T) { @@ -23,13 +24,13 @@ func TestParams(t *testing.T) { // Test custom params setting votePeriod := uint64(10) - voteThreshold := sdk.NewDecWithPrec(33, 2) + voteThreshold := math.LegacyNewDecWithPrec(33, 2) minVoters := uint64(4) - oracleRewardBand := sdk.NewDecWithPrec(1, 2) - slashFraction := sdk.NewDecWithPrec(1, 2) + oracleRewardBand := math.LegacyNewDecWithPrec(1, 2) + slashFraction := math.LegacyNewDecWithPrec(1, 2) slashWindow := uint64(1000) - minValidPerWindow := sdk.NewDecWithPrec(1, 4) - minFeeRatio := sdk.NewDecWithPrec(1, 2) + minValidPerWindow := math.LegacyNewDecWithPrec(1, 4) + minFeeRatio := math.LegacyNewDecWithPrec(1, 2) whitelist := []asset.Pair{ asset.Registry.Pair(denoms.BTC, denoms.NUSD), asset.Registry.Pair(denoms.ETH, denoms.NUSD), diff --git a/x/oracle/keeper/reward.go b/x/oracle/keeper/reward.go index 75c1fe751..c1f7f6480 100644 --- a/x/oracle/keeper/reward.go +++ b/x/oracle/keeper/reward.go @@ -3,9 +3,10 @@ package keeper import ( "github.com/NibiruChain/collections" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func (k Keeper) AllocateRewards(ctx sdk.Context, funderModule string, totalCoins sdk.Coins, votePeriods uint64) error { @@ -47,7 +48,7 @@ func (k Keeper) rewardWinners( continue } - rewardPortion, _ := totalRewards.MulDec(sdk.NewDec(validatorPerformance.RewardWeight).QuoInt64(totalRewardWeight)).TruncateDecimal() + rewardPortion, _ := totalRewards.MulDec(math.LegacyNewDec(validatorPerformance.RewardWeight).QuoInt64(totalRewardWeight)).TruncateDecimal() k.distrKeeper.AllocateTokensToValidator(ctx, validator, sdk.NewDecCoinsFromCoins(rewardPortion...)) distributedRewards = distributedRewards.Add(rewardPortion...) } diff --git a/x/oracle/keeper/reward_test.go b/x/oracle/keeper/reward_test.go index fe88d7808..4bbcfb84c 100644 --- a/x/oracle/keeper/reward_test.go +++ b/x/oracle/keeper/reward_test.go @@ -3,15 +3,16 @@ package keeper import ( "testing" + "cosmossdk.io/math" "github.com/NibiruChain/collections" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestKeeperRewardsDistributionMultiVotePeriods(t *testing.T) { @@ -28,8 +29,8 @@ func TestKeeperRewardsDistributionMultiVotePeriods(t *testing.T) { rewards := sdk.NewInt64Coin("reward", 1*common.TO_MICRO) valPeriodicRewards := sdk.NewDecCoinsFromCoins(rewards). - QuoDec(sdk.NewDec(int64(periods))). - QuoDec(sdk.NewDec(int64(validators))) + QuoDec(math.LegacyNewDec(int64(periods))). + QuoDec(math.LegacyNewDec(int64(validators))) AllocateRewards(t, fixture, sdk.NewCoins(rewards), periods) for i := uint64(1); i <= periods; i++ { @@ -50,8 +51,8 @@ func TestKeeperRewardsDistributionMultiVotePeriods(t *testing.T) { for valIndex := 0; valIndex < validators; valIndex++ { distributionRewards := fixture.DistrKeeper.GetValidatorOutstandingRewards(fixture.Ctx, ValAddrs[0]) truncatedGot, _ := distributionRewards.Rewards. - QuoDec(sdk.NewDec(int64(i))). // outstanding rewards will count for the previous vote period too, so we divide it by current period - TruncateDecimal() // NOTE: not applying this on truncatedExpected because of rounding the test fails + QuoDec(math.LegacyNewDec(int64(i))). // outstanding rewards will count for the previous vote period too, so we divide it by current period + TruncateDecimal() // NOTE: not applying this on truncatedExpected because of rounding the test fails truncatedExpected, _ := valPeriodicRewards.TruncateDecimal() require.Equalf(t, truncatedExpected, truncatedGot, "period: %d, %s <-> %s", i, truncatedExpected.String(), truncatedGot.String()) diff --git a/x/oracle/keeper/slash.go b/x/oracle/keeper/slash.go index d3de144f9..63d3885bd 100644 --- a/x/oracle/keeper/slash.go +++ b/x/oracle/keeper/slash.go @@ -1,6 +1,7 @@ package keeper import ( + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/NibiruChain/collections" @@ -13,7 +14,7 @@ func (k Keeper) SlashAndResetMissCounters(ctx sdk.Context) { // slash_window / vote_period votePeriodsPerWindow := uint64( - sdk.NewDec(int64(k.SlashWindow(ctx))). + math.LegacyNewDec(int64(k.SlashWindow(ctx))). QuoInt64(int64(k.VotePeriod(ctx))). TruncateInt64(), ) @@ -25,8 +26,8 @@ func (k Keeper) SlashAndResetMissCounters(ctx sdk.Context) { operator := mc.Key missCounter := mc.Value // Calculate valid vote rate; (SlashWindow - MissCounter)/SlashWindow - validVoteRate := sdk.NewDecFromInt( - sdk.NewInt(int64(votePeriodsPerWindow - missCounter))). + validVoteRate := math.LegacyNewDecFromInt( + math.NewInt(int64(votePeriodsPerWindow - missCounter))). QuoInt64(int64(votePeriodsPerWindow)) // Penalize the validator whose the valid vote rate is smaller than min threshold diff --git a/x/oracle/keeper/slash_test.go b/x/oracle/keeper/slash_test.go index bf7b86a28..8b851f458 100644 --- a/x/oracle/keeper/slash_test.go +++ b/x/oracle/keeper/slash_test.go @@ -3,6 +3,7 @@ package keeper import ( "testing" + "cosmossdk.io/math" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" sdk "github.com/cosmos/cosmos-sdk/types" @@ -12,9 +13,9 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestSlashAndResetMissCounters(t *testing.T) { @@ -44,7 +45,7 @@ func TestSlashAndResetMissCounters(t *testing.T) { ) require.Equal(t, amt, input.StakingKeeper.Validator(ctx, addr1).GetBondedTokens()) - votePeriodsPerWindow := sdk.NewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64() + votePeriodsPerWindow := math.LegacyNewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64() slashFraction := input.OracleKeeper.SlashFraction(input.Ctx) minValidVotes := input.OracleKeeper.MinValidPerWindow(input.Ctx).MulInt64(votePeriodsPerWindow).Ceil().TruncateInt64() // Case 1, no slash @@ -96,11 +97,11 @@ func TestInvalidVotesSlashing(t *testing.T) { input.OracleKeeper.Params.Set(input.Ctx, params) input.OracleKeeper.WhitelistedPairs.Insert(input.Ctx, asset.Registry.Pair(denoms.ATOM, denoms.USD)) - votePeriodsPerWindow := sdk.NewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64() + votePeriodsPerWindow := math.LegacyNewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64() slashFraction := input.OracleKeeper.SlashFraction(input.Ctx) minValidPerWindow := input.OracleKeeper.MinValidPerWindow(input.Ctx) - for i := uint64(0); i < uint64(sdk.OneDec().Sub(minValidPerWindow).MulInt64(votePeriodsPerWindow).TruncateInt64()); i++ { + for i := uint64(0); i < uint64(math.LegacyOneDec().Sub(minValidPerWindow).MulInt64(votePeriodsPerWindow).TruncateInt64()); i++ { input.Ctx = input.Ctx.WithBlockHeight(input.Ctx.BlockHeight() + 1) // Account 1, govstable @@ -110,7 +111,7 @@ func TestInvalidVotesSlashing(t *testing.T) { // Account 2, govstable, miss vote MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{ - {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: testExchangeRate.Add(sdk.NewDec(100000000000000))}, + {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: testExchangeRate.Add(math.LegacyNewDec(100000000000000))}, }, 1) // Account 3, govstable @@ -141,7 +142,7 @@ func TestInvalidVotesSlashing(t *testing.T) { // Account 2, govstable, miss vote MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{ - {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: testExchangeRate.Add(sdk.NewDec(100000000000000))}, + {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: testExchangeRate.Add(math.LegacyNewDec(100000000000000))}, }, 1) // Account 3, govstable @@ -160,7 +161,7 @@ func TestInvalidVotesSlashing(t *testing.T) { // input.OracleKeeper.UpdateExchangeRates(input.Ctx) validator = input.StakingKeeper.Validator(input.Ctx, ValAddrs[1]) - require.Equal(t, sdk.OneDec().Sub(slashFraction).MulInt(testStakingAmt).TruncateInt(), validator.GetBondedTokens()) + require.Equal(t, math.LegacyOneDec().Sub(slashFraction).MulInt(testStakingAmt).TruncateInt(), validator.GetBondedTokens()) } // TestWhitelistSlashing: Creates a scenario where one valoper (valIdx 0) does @@ -168,7 +169,7 @@ func TestInvalidVotesSlashing(t *testing.T) { func TestWhitelistSlashing(t *testing.T) { input, msgServer := Setup(t) - votePeriodsPerSlashWindow := sdk.NewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64() + votePeriodsPerSlashWindow := math.LegacyNewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64() minValidVotePeriodsPerWindow := input.OracleKeeper.MinValidPerWindow(input.Ctx) pair := asset.Registry.Pair(denoms.ATOM, denoms.USD) @@ -181,7 +182,7 @@ func TestWhitelistSlashing(t *testing.T) { perfs := input.OracleKeeper.UpdateExchangeRates(input.Ctx) require.EqualValues(t, 0, perfs.TotalRewardWeight()) - allowedMissPct := sdk.OneDec().Sub(minValidVotePeriodsPerWindow) + allowedMissPct := math.LegacyOneDec().Sub(minValidVotePeriodsPerWindow) allowedMissVotePeriods := allowedMissPct.MulInt64(votePeriodsPerSlashWindow). TruncateInt64() t.Logf("For %v blocks, valoper0 does not vote, while 1 and 2 do.", allowedMissVotePeriods) @@ -242,17 +243,17 @@ func TestAbstainSlashing(t *testing.T) { } input.OracleKeeper.WhitelistedPairs.Insert(input.Ctx, asset.Registry.Pair(denoms.ATOM, denoms.USD)) - votePeriodsPerWindow := sdk.NewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64() + votePeriodsPerWindow := math.LegacyNewDec(int64(input.OracleKeeper.SlashWindow(input.Ctx))).QuoInt64(int64(input.OracleKeeper.VotePeriod(input.Ctx))).TruncateInt64() minValidPerWindow := input.OracleKeeper.MinValidPerWindow(input.Ctx) - for i := uint64(0); i <= uint64(sdk.OneDec().Sub(minValidPerWindow).MulInt64(votePeriodsPerWindow).TruncateInt64()); i++ { + for i := uint64(0); i <= uint64(math.LegacyOneDec().Sub(minValidPerWindow).MulInt64(votePeriodsPerWindow).TruncateInt64()); i++ { input.Ctx = input.Ctx.WithBlockHeight(input.Ctx.BlockHeight() + 1) // Account 1, ATOM/USD MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{{Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: testExchangeRate}}, 0) // Account 2, ATOM/USD, abstain vote - MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{{Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: sdk.OneDec().Neg()}}, 1) + MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{{Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: math.LegacyOneDec().Neg()}}, 1) // Account 3, ATOM/USD MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{{Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: testExchangeRate}}, 2) diff --git a/x/oracle/keeper/test_utils.go b/x/oracle/keeper/test_utils.go index cc0b71761..50b65e8ba 100644 --- a/x/oracle/keeper/test_utils.go +++ b/x/oracle/keeper/test_utils.go @@ -5,17 +5,12 @@ import ( "testing" "time" - sudokeeper "github.com/NibiruChain/nibiru/x/sudo/keeper" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" - - "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/testutil/sims" - "github.com/cosmos/cosmos-sdk/types/module/testutil" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/types" + sdkmath "cosmossdk.io/math" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/sudo" + sudokeeper "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" dbm "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/secp256k1" @@ -25,8 +20,12 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/std" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/module/testutil" "github.com/cosmos/cosmos-sdk/x/auth" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" "github.com/cosmos/cosmos-sdk/x/auth/tx" @@ -37,6 +36,7 @@ import ( distr "github.com/cosmos/cosmos-sdk/x/distribution" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/params" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" @@ -56,6 +56,7 @@ var ModuleBasics = module.NewBasicManager( distr.AppModuleBasic{}, staking.AppModuleBasic{}, params.AppModuleBasic{}, + sudo.AppModuleBasic{}, ) // MakeTestCodec nolint @@ -159,7 +160,6 @@ func CreateTestFixture(t *testing.T) TestFixture { ms.MountStoreWithDB(keyOracle, storetypes.StoreTypeIAVL, db) ms.MountStoreWithDB(keyStaking, storetypes.StoreTypeIAVL, db) ms.MountStoreWithDB(keyDistr, storetypes.StoreTypeIAVL, db) - ms.MountStoreWithDB(keySudo, storetypes.StoreTypeIAVL, db) require.NoError(t, ms.LoadLatestVersion()) @@ -222,9 +222,7 @@ func CreateTestFixture(t *testing.T) TestFixture { distrKeeper.SetFeePool(ctx, distrtypes.InitialFeePool()) distrParams := distrtypes.DefaultParams() - distrParams.CommunityTax = sdk.NewDecWithPrec(2, 2) - distrParams.BaseProposerReward = sdk.NewDecWithPrec(1, 2) - distrParams.BonusProposerReward = sdk.NewDecWithPrec(4, 2) + distrParams.CommunityTax = sdkmath.LegacyNewDecWithPrec(2, 2) distrKeeper.SetParams(ctx, distrParams) stakingKeeper.SetHooks(stakingtypes.NewMultiStakingHooks(distrKeeper.Hooks())) @@ -236,11 +234,15 @@ func CreateTestFixture(t *testing.T) TestFixture { bankKeeper.SendCoinsFromModuleToModule(ctx, faucetAccountName, stakingtypes.NotBondedPoolName, sdk.NewCoins(sdk.NewCoin(denoms.NIBI, InitTokens.MulRaw(int64(len(Addrs)))))) + sudoKeeper := sudokeeper.NewKeeper(appCodec, keySudo) + sudoAcc := authtypes.NewEmptyModuleAccount(sudotypes.ModuleName) + accountKeeper.SetModuleAccount(ctx, feeCollectorAcc) accountKeeper.SetModuleAccount(ctx, bondPool) accountKeeper.SetModuleAccount(ctx, notBondedPool) accountKeeper.SetModuleAccount(ctx, distrAcc) accountKeeper.SetModuleAccount(ctx, oracleAcc) + accountKeeper.SetModuleAccount(ctx, sudoAcc) for _, addr := range Addrs { accountKeeper.SetAccount(ctx, authtypes.NewBaseAccountWithAddress(addr)) @@ -248,11 +250,6 @@ func CreateTestFixture(t *testing.T) TestFixture { require.NoError(t, err) } - sudoKeeper := sudokeeper.NewKeeper( - appCodec, - keySudo, - ) - keeper := NewKeeper( appCodec, keyOracle, @@ -261,6 +258,7 @@ func CreateTestFixture(t *testing.T) TestFixture { distrKeeper, stakingKeeper, slashingKeeper, + sudoKeeper, distrtypes.ModuleName, ) @@ -272,15 +270,23 @@ func CreateTestFixture(t *testing.T) TestFixture { keeper.Params.Set(ctx, defaults) - return TestFixture{ctx, legacyAmino, accountKeeper, bankKeeper, keeper, *stakingKeeper, distrKeeper, sudoKeeper} + return TestFixture{ + ctx, legacyAmino, accountKeeper, bankKeeper, + keeper, + *stakingKeeper, + distrKeeper, + sudoKeeper, + } } // NewTestMsgCreateValidator test msg creator -func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey cryptotypes.PubKey, amt sdk.Int) *stakingtypes.MsgCreateValidator { - commission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) +func NewTestMsgCreateValidator( + address sdk.ValAddress, pubKey cryptotypes.PubKey, amt sdkmath.Int, +) *stakingtypes.MsgCreateValidator { + commission := stakingtypes.NewCommissionRates(sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec(), sdkmath.LegacyZeroDec()) msg, _ := stakingtypes.NewMsgCreateValidator( address, pubKey, sdk.NewCoin(denoms.NIBI, amt), - stakingtypes.Description{}, commission, sdk.OneInt(), + stakingtypes.Description{}, commission, sdkmath.OneInt(), ) return msg @@ -304,7 +310,7 @@ func AllocateRewards(t *testing.T, input TestFixture, rewards sdk.Coins, votePer var ( testStakingAmt = sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction) - testExchangeRate = sdk.NewDec(1700) + testExchangeRate = sdkmath.LegacyNewDec(1700) ) func Setup(t *testing.T) (TestFixture, types.MsgServer) { diff --git a/x/oracle/keeper/update_exchange_rates.go b/x/oracle/keeper/update_exchange_rates.go index 99a1251b1..177b4eaba 100644 --- a/x/oracle/keeper/update_exchange_rates.go +++ b/x/oracle/keeper/update_exchange_rates.go @@ -5,10 +5,10 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/omap" - "github.com/NibiruChain/nibiru/x/common/set" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/omap" + "github.com/NibiruChain/nibiru/v2/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) // UpdateExchangeRates updates the ExchangeRates, this is supposed to be executed on EndBlock. @@ -30,6 +30,18 @@ func (k Keeper) UpdateExchangeRates(ctx sdk.Context) types.ValidatorPerformances params, _ := k.Params.Get(ctx) k.clearVotesAndPrevotes(ctx, params.VotePeriod) k.refreshWhitelist(ctx, params.Whitelist, whitelistedPairs) + + for _, validatorPerformance := range validatorPerformances { + _ = ctx.EventManager().EmitTypedEvent(&types.EventValidatorPerformance{ + Validator: validatorPerformance.ValAddress.String(), + VotingPower: validatorPerformance.Power, + RewardWeight: validatorPerformance.RewardWeight, + WinCount: validatorPerformance.WinCount, + AbstainCount: validatorPerformance.AbstainCount, + MissCount: validatorPerformance.MissCount, + }) + } + return validatorPerformances } @@ -74,7 +86,7 @@ func (k Keeper) tallyVotesAndUpdatePrices( ) { rewardBand := k.RewardBand(ctx) // Iterate through sorted keys for deterministic ordering. - orderedPairVotes := omap.OrderedMap_Pair[types.ExchangeRateVotes](pairVotes) + orderedPairVotes := omap.SortedMap_Pair[types.ExchangeRateVotes](pairVotes) for pair := range orderedPairVotes.Range() { exchangeRate := Tally(pairVotes[pair], rewardBand, validatorPerformances) k.SetPrice(ctx, pair, exchangeRate) diff --git a/x/oracle/keeper/update_exchange_rates_test.go b/x/oracle/keeper/update_exchange_rates_test.go index eec7dbd18..77f10ce39 100644 --- a/x/oracle/keeper/update_exchange_rates_test.go +++ b/x/oracle/keeper/update_exchange_rates_test.go @@ -12,12 +12,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + sdkmath "cosmossdk.io/math" "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestOracleThreshold(t *testing.T) { @@ -137,7 +138,7 @@ func TestOracleTally(t *testing.T) { h := NewMsgServerImpl(fixture.OracleKeeper, fixture.SudoKeeper) for i, rate := range rates { - decExchangeRate := sdk.NewDecWithPrec(int64(rate*math.Pow10(OracleDecPrecision)), int64(OracleDecPrecision)) + decExchangeRate := sdkmath.LegacyNewDecWithPrec(int64(rate*math.Pow10(OracleDecPrecision)), int64(OracleDecPrecision)) exchangeRateStr, err := types.ExchangeRateTuples{ {ExchangeRate: decExchangeRate, Pair: asset.Registry.Pair(denoms.BTC, denoms.USD)}, }.ToString() @@ -261,7 +262,7 @@ func TestOracleRewardBand(t *testing.T) { // Account 1 will miss the vote due to raward band condition // Account 1, atom:usd MakeAggregatePrevoteAndVote(t, fixture, msgServer, 0, types.ExchangeRateTuples{ - {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: testExchangeRate.Sub(rewardSpread.Add(sdk.OneDec()))}, + {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: testExchangeRate.Sub(rewardSpread.Add(sdkmath.LegacyOneDec()))}, }, 0) // Account 2, atom:usd @@ -301,7 +302,7 @@ func TestOracleMultiRewardDistribution(t *testing.T) { // Account 3, KRW makeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{{Pair: common.Pairbtc:usd.String(), ExchangeRate: randomExchangeRate}}, 2) - rewardAmt := sdk.NewInt(1e6) + rewardAmt := math.NewInt(1e6) err := input.BankKeeper.MintCoins(input.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewCoin(denoms.Gov, rewardAmt))) require.NoError(t, err) @@ -309,9 +310,9 @@ func TestOracleMultiRewardDistribution(t *testing.T) { rewardDistributedWindow := input.OracleKeeper.RewardDistributionWindow(input.Ctx) - expectedRewardAmt := sdk.NewDecFromInt(rewardAmt.QuoRaw(3).MulRaw(2)).QuoInt64(int64(rewardDistributedWindow)).TruncateInt() - expectedRewardAmt2 := sdk.ZeroInt() // even vote power is same KRW with SDR, KRW chosen referenceTerra because alphabetical order - expectedRewardAmt3 := sdk.NewDecFromInt(rewardAmt.QuoRaw(3)).QuoInt64(int64(rewardDistributedWindow)).TruncateInt() + expectedRewardAmt := math.LegacyNewDecFromInt(rewardAmt.QuoRaw(3).MulRaw(2)).QuoInt64(int64(rewardDistributedWindow)).TruncateInt() + expectedRewardAmt2 := math.ZeroInt() // even vote power is same KRW with SDR, KRW chosen referenceTerra because alphabetical order + expectedRewardAmt3 := math.LegacyNewDecFromInt(rewardAmt.QuoRaw(3)).QuoInt64(int64(rewardDistributedWindow)).TruncateInt() rewards := input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), ValAddrs[0]) assert.Equal(t, expectedRewardAmt, rewards.Rewards.AmountOf(denoms.Gov).TruncateInt()) @@ -327,9 +328,9 @@ func TestOracleExchangeRate(t *testing.T) { // eth:usd and atom:usd pass, but btc:usd fails due to not enough validators voting. input, h := Setup(t) - atomUsdExchangeRate := sdk.NewDec(1000000) - ethUsdExchangeRate := sdk.NewDec(1000000) - btcusdExchangeRate := sdk.NewDec(1e6) + atomUsdExchangeRate := sdkmath.LegacyNewDec(1000000) + ethUsdExchangeRate := sdkmath.LegacyNewDec(1000000) + btcusdExchangeRate := sdkmath.LegacyNewDec(1e6) // Account 1, eth:usd, atom:usd, btc:usd MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{ @@ -348,14 +349,14 @@ func TestOracleExchangeRate(t *testing.T) { MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{ {Pair: asset.Registry.Pair(denoms.ETH, denoms.USD), ExchangeRate: ethUsdExchangeRate}, {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: atomUsdExchangeRate}, - {Pair: asset.Registry.Pair(denoms.BTC, denoms.USD), ExchangeRate: sdk.ZeroDec()}, + {Pair: asset.Registry.Pair(denoms.BTC, denoms.USD), ExchangeRate: sdkmath.LegacyZeroDec()}, }, 2) // Account 4, eth:usd, atom:usd, btc:usd MakeAggregatePrevoteAndVote(t, input, h, 0, types.ExchangeRateTuples{ {Pair: asset.Registry.Pair(denoms.ETH, denoms.USD), ExchangeRate: ethUsdExchangeRate}, {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: atomUsdExchangeRate}, - {Pair: asset.Registry.Pair(denoms.BTC, denoms.USD), ExchangeRate: sdk.ZeroDec()}, + {Pair: asset.Registry.Pair(denoms.BTC, denoms.USD), ExchangeRate: sdkmath.LegacyZeroDec()}, }, 3) ethUsdRewards := sdk.NewInt64Coin("ETHREWARD", 1*common.TO_MICRO) @@ -370,8 +371,8 @@ func TestOracleExchangeRate(t *testing.T) { // val 1,2,3,4 all won on 2 pairs // so total votes are 2 * 2 + 2 + 2 = 8 expectedRewardAmt := sdk.NewDecCoinsFromCoins(ethUsdRewards, atomUsdRewards). - QuoDec(sdk.NewDec(8)). // total votes - MulDec(sdk.NewDec(2)) // votes won by val1 and val2 + QuoDec(sdkmath.LegacyNewDec(8)). // total votes + MulDec(sdkmath.LegacyNewDec(2)) // votes won by val1 and val2 rewards := input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), ValAddrs[0]) assert.Equalf(t, expectedRewardAmt, rewards.Rewards, "%s <-> %s", expectedRewardAmt, rewards.Rewards) rewards = input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), ValAddrs[1]) @@ -388,8 +389,8 @@ func TestOracleRandomPrices(t *testing.T) { for i := 0; i < 100; i++ { for val := 0; val < 4; val++ { MakeAggregatePrevoteAndVote(t, fixture, msgServer, 0, types.ExchangeRateTuples{ - {Pair: asset.Registry.Pair(denoms.ETH, denoms.USD), ExchangeRate: sdk.NewDec(int64(rand.Uint64() % 1e6))}, - {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: sdk.NewDec(int64(rand.Uint64() % 1e6))}, + {Pair: asset.Registry.Pair(denoms.ETH, denoms.USD), ExchangeRate: sdkmath.LegacyNewDec(int64(rand.Uint64() % 1e6))}, + {Pair: asset.Registry.Pair(denoms.ATOM, denoms.USD), ExchangeRate: sdkmath.LegacyNewDec(int64(rand.Uint64() % 1e6))}, }, val) } diff --git a/x/oracle/keeper/whitelist.go b/x/oracle/keeper/whitelist.go index f1662c19e..224b7d5ca 100644 --- a/x/oracle/keeper/whitelist.go +++ b/x/oracle/keeper/whitelist.go @@ -5,8 +5,8 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/set" ) // IsWhitelistedPair returns existence of a pair in the voting target list diff --git a/x/oracle/keeper/whitelist_test.go b/x/oracle/keeper/whitelist_test.go index 6061d73e3..5f24606df 100644 --- a/x/oracle/keeper/whitelist_test.go +++ b/x/oracle/keeper/whitelist_test.go @@ -9,9 +9,9 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/set" ) func TestKeeper_GetVoteTargets(t *testing.T) { diff --git a/x/oracle/module.go b/x/oracle/module.go index 18c0d6f71..6bd34f484 100644 --- a/x/oracle/module.go +++ b/x/oracle/module.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - sudokeeper "github.com/NibiruChain/nibiru/x/sudo/keeper" + sudokeeper "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" @@ -20,10 +20,10 @@ import ( "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" - "github.com/NibiruChain/nibiru/x/oracle/client/cli" - "github.com/NibiruChain/nibiru/x/oracle/keeper" - "github.com/NibiruChain/nibiru/x/oracle/simulation" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/cli" + "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + "github.com/NibiruChain/nibiru/v2/x/oracle/simulation" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) var ( diff --git a/x/oracle/simulation/decoder.go b/x/oracle/simulation/decoder.go index 25935f631..0400b1552 100644 --- a/x/oracle/simulation/decoder.go +++ b/x/oracle/simulation/decoder.go @@ -10,7 +10,7 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's diff --git a/x/oracle/simulation/decoder_test.go b/x/oracle/simulation/decoder_test.go index 84c44e344..593d05294 100644 --- a/x/oracle/simulation/decoder_test.go +++ b/x/oracle/simulation/decoder_test.go @@ -4,17 +4,18 @@ import ( "fmt" "testing" + "cosmossdk.io/math" "github.com/cometbft/cometbft/crypto/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" gogotypes "github.com/cosmos/gogoproto/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/keeper" - sim "github.com/NibiruChain/nibiru/x/oracle/simulation" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + sim "github.com/NibiruChain/nibiru/v2/x/oracle/simulation" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) var ( @@ -27,13 +28,13 @@ func TestDecodeDistributionStore(t *testing.T) { cdc := keeper.MakeTestCodec(t) dec := sim.NewDecodeStore(cdc) - exchangeRate := sdk.NewDecWithPrec(1234, 1) + exchangeRate := math.LegacyNewDecWithPrec(1234, 1) missCounter := uint64(23) aggregatePrevote := types.NewAggregateExchangeRatePrevote(types.AggregateVoteHash([]byte("12345")), valAddr, 123) aggregateVote := types.NewAggregateExchangeRateVote(types.ExchangeRateTuples{ - {Pair: asset.Registry.Pair(denoms.NIBI, denoms.NUSD), ExchangeRate: sdk.NewDecWithPrec(1234, 1)}, - {Pair: asset.Registry.Pair(denoms.ETH, denoms.NUSD), ExchangeRate: sdk.NewDecWithPrec(4321, 1)}, + {Pair: asset.Registry.Pair(denoms.NIBI, denoms.NUSD), ExchangeRate: math.LegacyNewDecWithPrec(1234, 1)}, + {Pair: asset.Registry.Pair(denoms.ETH, denoms.NUSD), ExchangeRate: math.LegacyNewDecWithPrec(4321, 1)}, }, valAddr) pair := "btc:usd" diff --git a/x/oracle/simulation/genesis.go b/x/oracle/simulation/genesis.go index 4ad41260f..ad0a3a017 100644 --- a/x/oracle/simulation/genesis.go +++ b/x/oracle/simulation/genesis.go @@ -7,13 +7,14 @@ import ( "fmt" "math/rand" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) // Simulation parameter constants @@ -32,12 +33,12 @@ func GenVotePeriod(r *rand.Rand) uint64 { // GenVoteThreshold randomized VoteThreshold func GenVoteThreshold(r *rand.Rand) sdk.Dec { - return sdk.NewDecWithPrec(333, 3).Add(sdk.NewDecWithPrec(int64(r.Intn(333)), 3)) + return math.LegacyNewDecWithPrec(333, 3).Add(math.LegacyNewDecWithPrec(int64(r.Intn(333)), 3)) } // GenRewardBand randomized RewardBand func GenRewardBand(r *rand.Rand) sdk.Dec { - return sdk.ZeroDec().Add(sdk.NewDecWithPrec(int64(r.Intn(100)), 3)) + return math.LegacyZeroDec().Add(math.LegacyNewDecWithPrec(int64(r.Intn(100)), 3)) } // GenRewardDistributionWindow randomized RewardDistributionWindow @@ -47,7 +48,7 @@ func GenRewardDistributionWindow(r *rand.Rand) uint64 { // GenSlashFraction randomized SlashFraction func GenSlashFraction(r *rand.Rand) sdk.Dec { - return sdk.ZeroDec().Add(sdk.NewDecWithPrec(int64(r.Intn(100)), 3)) + return math.LegacyZeroDec().Add(math.LegacyNewDecWithPrec(int64(r.Intn(100)), 3)) } // GenSlashWindow randomized SlashWindow @@ -57,7 +58,7 @@ func GenSlashWindow(r *rand.Rand) uint64 { // GenMinValidPerWindow randomized MinValidPerWindow func GenMinValidPerWindow(r *rand.Rand) sdk.Dec { - return sdk.ZeroDec().Add(sdk.NewDecWithPrec(int64(r.Intn(500)), 3)) + return math.LegacyZeroDec().Add(math.LegacyNewDecWithPrec(int64(r.Intn(500)), 3)) } // RandomizedGenState generates a random GenesisState for oracle @@ -108,7 +109,7 @@ func RandomizedGenState(simState *module.SimulationState) { MinValidPerWindow: minValidPerWindow, }, []types.ExchangeRateTuple{ - {Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), ExchangeRate: sdk.NewDec(20_000)}, + {Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), ExchangeRate: math.LegacyNewDec(20_000)}, }, []types.FeederDelegation{}, []types.MissCounter{}, diff --git a/x/oracle/simulation/operations.go b/x/oracle/simulation/operations.go index 6db5cf5be..935520c1b 100644 --- a/x/oracle/simulation/operations.go +++ b/x/oracle/simulation/operations.go @@ -6,11 +6,12 @@ import ( "math/rand" "strings" + "cosmossdk.io/math" "github.com/CosmWasm/wasmd/app/params" "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" helpers "github.com/cosmos/cosmos-sdk/testutil/sims" @@ -20,8 +21,8 @@ import ( simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/NibiruChain/nibiru/x/oracle/keeper" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/keeper" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) // Simulation operation weights constants @@ -102,7 +103,7 @@ func SimulateMsgAggregateExchangeRatePrevote(ak types.AccountKeeper, bk types.Ba exchangeRatesStr := "" for _, pair := range whitelist { - price := sdk.NewDecWithPrec(int64(simtypes.RandIntBetween(r, 1, 10000)), int64(1)) + price := math.LegacyNewDecWithPrec(int64(simtypes.RandIntBetween(r, 1, 10000)), int64(1)) exchangeRatesStr += price.String() + pair.String() + "," } diff --git a/x/oracle/types/ballot.go b/x/oracle/types/ballot.go index e0784ef21..90276f402 100644 --- a/x/oracle/types/ballot.go +++ b/x/oracle/types/ballot.go @@ -2,14 +2,13 @@ package types import ( "encoding/json" - "fmt" - "math" "sort" - "strconv" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/asset" ) // NOTE: we don't need to implement proto interface on this file @@ -57,7 +56,7 @@ func (pb ExchangeRateVotes) ToCrossRate(bases map[string]sdk.Dec) (cb ExchangeRa vote.ExchangeRate = exchangeRateRT.Quo(vote.ExchangeRate) } else { // If we can't get reference exchange rate, we just convert the vote as abstain vote - vote.ExchangeRate = sdk.ZeroDec() + vote.ExchangeRate = math.LegacyZeroDec() vote.Power = 0 } @@ -103,7 +102,7 @@ func (votes ExchangeRateVotes) WeightedMedian() sdk.Dec { } } } - return sdk.ZeroDec() + return math.LegacyZeroDec() } // WeightedMedianWithAssertion returns the median weighted by the power of the ExchangeRateVote. @@ -121,22 +120,22 @@ func (pb ExchangeRateVotes) WeightedMedianWithAssertion() sdk.Dec { } } } - return sdk.ZeroDec() + return math.LegacyZeroDec() } // StandardDeviation returns the standard deviation by the power of the ExchangeRateVote. func (pb ExchangeRateVotes) StandardDeviation(median sdk.Dec) (standardDeviation sdk.Dec) { if len(pb) == 0 { - return sdk.ZeroDec() + return math.LegacyZeroDec() } defer func() { if e := recover(); e != nil { - standardDeviation = sdk.ZeroDec() + standardDeviation = math.LegacyZeroDec() } }() - sum := sdk.ZeroDec() + sum := math.LegacyZeroDec() n := 0 for _, v := range pb { // ignore abstain votes in std dev calculation @@ -149,9 +148,10 @@ func (pb ExchangeRateVotes) StandardDeviation(median sdk.Dec) (standardDeviation variance := sum.QuoInt64(int64(n)) - floatNum, _ := strconv.ParseFloat(variance.String(), 64) - floatNum = math.Sqrt(floatNum) - standardDeviation, _ = sdk.NewDecFromStr(fmt.Sprintf("%f", floatNum)) + standardDeviation, err := common.SqrtDec(variance) + if err != nil { + return math.LegacyZeroDec() + } return } diff --git a/x/oracle/types/ballot_test.go b/x/oracle/types/ballot_test.go index 58de574bc..af19bd2c7 100644 --- a/x/oracle/types/ballot_test.go +++ b/x/oracle/types/ballot_test.go @@ -2,22 +2,24 @@ package types_test import ( "fmt" - "math" + basicmath "math" "sort" "strconv" "testing" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" "github.com/stretchr/testify/require" "github.com/cometbft/cometbft/crypto/secp256k1" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestExchangeRateVotesToMap(t *testing.T) { @@ -29,19 +31,19 @@ func TestExchangeRateVotesToMap(t *testing.T) { { Voter: sdk.ValAddress(secp256k1.GenPrivKey().PubKey().Address()), Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), - ExchangeRate: sdk.NewDec(1600), + ExchangeRate: math.LegacyNewDec(1600), Power: 100, }, { Voter: sdk.ValAddress(secp256k1.GenPrivKey().PubKey().Address()), Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), - ExchangeRate: sdk.ZeroDec(), + ExchangeRate: math.LegacyZeroDec(), Power: 100, }, { Voter: sdk.ValAddress(secp256k1.GenPrivKey().PubKey().Address()), Pair: asset.Registry.Pair(denoms.BTC, denoms.NUSD), - ExchangeRate: sdk.NewDec(1500), + ExchangeRate: math.LegacyNewDec(1500), Power: 100, }, }, @@ -71,19 +73,19 @@ func TestToCrossRate(t *testing.T) { expected sdk.Dec }{ { - base: sdk.NewDec(1600), - quote: sdk.NewDec(100), - expected: sdk.NewDec(16), + base: math.LegacyNewDec(1600), + quote: math.LegacyNewDec(100), + expected: math.LegacyNewDec(16), }, { - base: sdk.ZeroDec(), - quote: sdk.NewDec(100), - expected: sdk.NewDec(16), + base: math.LegacyZeroDec(), + quote: math.LegacyNewDec(100), + expected: math.LegacyNewDec(16), }, { - base: sdk.NewDec(1600), - quote: sdk.ZeroDec(), - expected: sdk.NewDec(16), + base: math.LegacyNewDec(1600), + quote: math.LegacyZeroDec(), + expected: math.LegacyNewDec(16), }, } @@ -101,7 +103,7 @@ func TestToCrossRate(t *testing.T) { if !data.base.IsZero() && !data.quote.IsZero() { cb = append(cb, types.NewExchangeRateVote(data.base.Quo(data.quote), asset.Registry.Pair(denoms.BTC, denoms.NUSD), valAddr, 100)) } else { - cb = append(cb, types.NewExchangeRateVote(sdk.ZeroDec(), asset.Registry.Pair(denoms.BTC, denoms.NUSD), valAddr, 0)) + cb = append(cb, types.NewExchangeRateVote(math.LegacyZeroDec(), asset.Registry.Pair(denoms.BTC, denoms.NUSD), valAddr, 0)) } } @@ -112,15 +114,15 @@ func TestToCrossRate(t *testing.T) { } func TestSqrt(t *testing.T) { - num := sdk.NewDecWithPrec(144, 4) + num := math.LegacyNewDecWithPrec(144, 4) floatNum, err := strconv.ParseFloat(num.String(), 64) require.NoError(t, err) - floatNum = math.Sqrt(floatNum) - num, err = sdk.NewDecFromStr(fmt.Sprintf("%f", floatNum)) + floatNum = basicmath.Sqrt(floatNum) + num, err = math.LegacyNewDecFromStr(fmt.Sprintf("%f", floatNum)) require.NoError(t, err) - require.Equal(t, sdk.NewDecWithPrec(12, 2), num) + require.Equal(t, math.LegacyNewDecWithPrec(12, 2), num) } func TestPBPower(t *testing.T) { @@ -132,7 +134,7 @@ func TestPBPower(t *testing.T) { for i := 0; i < len(sk.Validators()); i++ { power := sk.Validator(ctx, valAccAddrs[i]).GetConsensusPower(sdk.DefaultPowerReduction) vote := types.NewExchangeRateVote( - sdk.ZeroDec(), + math.LegacyZeroDec(), asset.Registry.Pair(denoms.ETH, denoms.NUSD), valAccAddrs[i], power, @@ -151,7 +153,7 @@ func TestPBPower(t *testing.T) { pubKey := secp256k1.GenPrivKey().PubKey() faceValAddr := sdk.ValAddress(pubKey.Address()) fakeVote := types.NewExchangeRateVote( - sdk.OneDec(), + math.LegacyOneDec(), asset.Registry.Pair(denoms.ETH, denoms.NUSD), faceValAddr, 0, @@ -173,35 +175,35 @@ func TestPBWeightedMedian(t *testing.T) { []int64{1, 2, 10, 100000}, []int64{1, 1, 100, 1}, []bool{true, true, true, true}, - sdk.NewDec(10), + math.LegacyNewDec(10), }, { // Adding fake validator doesn't change outcome []int64{1, 2, 10, 100000, 10000000000}, []int64{1, 1, 100, 1, 10000}, []bool{true, true, true, true, false}, - sdk.NewDec(10), + math.LegacyNewDec(10), }, { // Tie votes []int64{1, 2, 3, 4}, []int64{1, 100, 100, 1}, []bool{true, true, true, true}, - sdk.NewDec(2), + math.LegacyNewDec(2), }, { // No votes []int64{}, []int64{}, []bool{true, true, true, true}, - sdk.ZeroDec(), + math.LegacyZeroDec(), }, { // not sorted []int64{2, 1, 10, 100000}, []int64{1, 1, 100, 1}, []bool{true, true, true, true}, - sdk.NewDec(10), + math.LegacyNewDec(10), }, } @@ -216,7 +218,7 @@ func TestPBWeightedMedian(t *testing.T) { } vote := types.NewExchangeRateVote( - sdk.NewDec(int64(input)), + math.LegacyNewDec(int64(input)), asset.Registry.Pair(denoms.ETH, denoms.NUSD), valAddr, power, @@ -242,39 +244,39 @@ func TestPBStandardDeviation(t *testing.T) { []float64{1.0, 2.0, 10.0, 100000.0}, []int64{1, 1, 100, 1}, []bool{true, true, true, true}, - sdk.NewDecWithPrec(4999500036300, types.OracleDecPrecision), + math.LegacyMustNewDecFromStr("49995.000362536000000000"), }, { // Adding fake validator doesn't change outcome []float64{1.0, 2.0, 10.0, 100000.0, 10000000000}, []int64{1, 1, 100, 1, 10000}, []bool{true, true, true, true, false}, - sdk.NewDecWithPrec(447213595075100600, types.OracleDecPrecision), + math.LegacyMustNewDecFromStr("4472135950.751005519000000000"), }, { // Tie votes []float64{1.0, 2.0, 3.0, 4.0}, []int64{1, 100, 100, 1}, []bool{true, true, true, true}, - sdk.NewDecWithPrec(122474500, types.OracleDecPrecision), + math.LegacyMustNewDecFromStr("1.224744871000000000"), }, { // No votes []float64{}, []int64{}, []bool{true, true, true, true}, - sdk.NewDecWithPrec(0, 0), + math.LegacyNewDecWithPrec(0, 0), }, { // Abstain votes are ignored []float64{1.0, 2.0, 10.0, 100000.0, -99999999999.0, 0}, []int64{1, 1, 100, 1, 1, 1}, []bool{true, true, true, true, true, true}, - sdk.NewDecWithPrec(4999500036300, types.OracleDecPrecision), + math.LegacyMustNewDecFromStr("49995.000362536000000000"), }, } - base := math.Pow10(types.OracleDecPrecision) + base := basicmath.Pow10(types.OracleDecPrecision) for _, tc := range tests { pb := types.ExchangeRateVotes{} for i, input := range tc.inputs { @@ -286,7 +288,7 @@ func TestPBStandardDeviation(t *testing.T) { } vote := types.NewExchangeRateVote( - sdk.NewDecWithPrec(int64(input*base), int64(types.OracleDecPrecision)), + math.LegacyNewDecWithPrec(int64(input*base), int64(types.OracleDecPrecision)), asset.Registry.Pair(denoms.ETH, denoms.NUSD), valAddr, power, @@ -301,11 +303,11 @@ func TestPBStandardDeviation(t *testing.T) { func TestPBStandardDeviationOverflow(t *testing.T) { valAddr := sdk.ValAddress(secp256k1.GenPrivKey().PubKey().Address()) - exchangeRate, err := sdk.NewDecFromStr("100000000000000000000000000000000000000000000000000000000.0") + exchangeRate, err := math.LegacyNewDecFromStr("100000000000000000000000000000000000000000000000000000000.0") require.NoError(t, err) pb := types.ExchangeRateVotes{types.NewExchangeRateVote( - sdk.ZeroDec(), + math.LegacyZeroDec(), asset.Registry.Pair(denoms.ETH, denoms.NUSD), valAddr, 2, @@ -316,7 +318,7 @@ func TestPBStandardDeviationOverflow(t *testing.T) { 1, )} - require.Equal(t, sdk.ZeroDec(), pb.StandardDeviation(pb.WeightedMedianWithAssertion())) + require.Equal(t, math.LegacyZeroDec(), pb.StandardDeviation(pb.WeightedMedianWithAssertion())) } func TestNewClaim(t *testing.T) { diff --git a/x/oracle/types/errors.go b/x/oracle/types/errors.go index 23fb21fa6..95332e116 100644 --- a/x/oracle/types/errors.go +++ b/x/oracle/types/errors.go @@ -2,25 +2,37 @@ package types import ( "fmt" + "sync/atomic" "github.com/cometbft/cometbft/crypto/tmhash" sdkerrors "cosmossdk.io/errors" ) +var moduleErrorCodeIdx uint32 = 1 + +// registerError: Cleaner way of using 'sdkerrors.Register' without as much time +// manually writing integers. +func registerError(msg string) *sdkerrors.Error { + // Atomic for thread safety on concurrent calls + atomic.AddUint32(&moduleErrorCodeIdx, 1) + return sdkerrors.Register(ModuleName, moduleErrorCodeIdx, msg) +} + // Oracle Errors var ( - ErrInvalidExchangeRate = sdkerrors.Register(ModuleName, 2, "invalid exchange rate") - ErrNoPrevote = sdkerrors.Register(ModuleName, 3, "no prevote") - ErrNoVote = sdkerrors.Register(ModuleName, 4, "no vote") - ErrNoVotingPermission = sdkerrors.Register(ModuleName, 5, "unauthorized voter") - ErrInvalidHash = sdkerrors.Register(ModuleName, 6, "invalid hash") - ErrInvalidHashLength = sdkerrors.Register(ModuleName, 7, fmt.Sprintf("invalid hash length; should equal %d", tmhash.TruncatedSize)) - ErrVerificationFailed = sdkerrors.Register(ModuleName, 8, "hash verification failed") - ErrRevealPeriodMissMatch = sdkerrors.Register(ModuleName, 9, "reveal period of submitted vote do not match with registered prevote") - ErrInvalidSaltLength = sdkerrors.Register(ModuleName, 10, "invalid salt length; should be 1~4") - ErrNoAggregatePrevote = sdkerrors.Register(ModuleName, 11, "no aggregate prevote") - ErrNoAggregateVote = sdkerrors.Register(ModuleName, 12, "no aggregate vote") - ErrUnknownPair = sdkerrors.Register(ModuleName, 13, "unknown pair") - ErrNoValidTWAP = sdkerrors.Register(ModuleName, 14, "TWA price not found") + ErrInvalidExchangeRate = registerError("invalid exchange rate") + ErrNoPrevote = registerError("no prevote") + ErrNoVote = registerError("no vote") + ErrNoVotingPermission = registerError("unauthorized voter") + ErrInvalidHash = registerError("invalid hash") + ErrInvalidHashLength = registerError( + fmt.Sprintf("invalid hash length; should equal %d", tmhash.TruncatedSize)) + ErrHashVerificationFailed = registerError("hash verification failed") + ErrRevealPeriodMissMatch = registerError("reveal period of submitted vote do not match with registered prevote") + ErrInvalidSaltLength = registerError("invalid salt length; should be 1~4") + ErrNoAggregatePrevote = registerError("no aggregate prevote") + ErrNoAggregateVote = registerError("no aggregate vote") + ErrUnknownPair = registerError("unknown pair") + ErrNoValidTWAP = registerError("TWA price not found") ) diff --git a/x/oracle/types/event.pb.go b/x/oracle/types/event.pb.go index 085a6c8ef..990920c18 100644 --- a/x/oracle/types/event.pb.go +++ b/x/oracle/types/event.pb.go @@ -256,43 +256,142 @@ func (m *EventAggregatePrevote) GetFeeder() string { return "" } +type EventValidatorPerformance struct { + // Validator is the Bech32 address to which the vote will be credited. + Validator string `protobuf:"bytes,1,opt,name=validator,proto3" json:"validator,omitempty"` + // Tendermint consensus voting power + VotingPower int64 `protobuf:"varint,2,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` + // RewardWeight: Weight of rewards the validator should receive in units of + // consensus power. + RewardWeight int64 `protobuf:"varint,3,opt,name=reward_weight,json=rewardWeight,proto3" json:"reward_weight,omitempty"` + // Number of valid votes for which the validator will be rewarded + WinCount int64 `protobuf:"varint,4,opt,name=win_count,json=winCount,proto3" json:"win_count,omitempty"` + // Number of abstained votes for which there will be no reward or punishment + AbstainCount int64 `protobuf:"varint,5,opt,name=abstain_count,json=abstainCount,proto3" json:"abstain_count,omitempty"` + // Number of invalid/punishable votes + MissCount int64 `protobuf:"varint,6,opt,name=miss_count,json=missCount,proto3" json:"miss_count,omitempty"` +} + +func (m *EventValidatorPerformance) Reset() { *m = EventValidatorPerformance{} } +func (m *EventValidatorPerformance) String() string { return proto.CompactTextString(m) } +func (*EventValidatorPerformance) ProtoMessage() {} +func (*EventValidatorPerformance) Descriptor() ([]byte, []int) { + return fileDescriptor_94ec441b793fc0ea, []int{4} +} +func (m *EventValidatorPerformance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventValidatorPerformance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventValidatorPerformance.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventValidatorPerformance) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventValidatorPerformance.Merge(m, src) +} +func (m *EventValidatorPerformance) XXX_Size() int { + return m.Size() +} +func (m *EventValidatorPerformance) XXX_DiscardUnknown() { + xxx_messageInfo_EventValidatorPerformance.DiscardUnknown(m) +} + +var xxx_messageInfo_EventValidatorPerformance proto.InternalMessageInfo + +func (m *EventValidatorPerformance) GetValidator() string { + if m != nil { + return m.Validator + } + return "" +} + +func (m *EventValidatorPerformance) GetVotingPower() int64 { + if m != nil { + return m.VotingPower + } + return 0 +} + +func (m *EventValidatorPerformance) GetRewardWeight() int64 { + if m != nil { + return m.RewardWeight + } + return 0 +} + +func (m *EventValidatorPerformance) GetWinCount() int64 { + if m != nil { + return m.WinCount + } + return 0 +} + +func (m *EventValidatorPerformance) GetAbstainCount() int64 { + if m != nil { + return m.AbstainCount + } + return 0 +} + +func (m *EventValidatorPerformance) GetMissCount() int64 { + if m != nil { + return m.MissCount + } + return 0 +} + func init() { proto.RegisterType((*EventPriceUpdate)(nil), "nibiru.oracle.v1.EventPriceUpdate") proto.RegisterType((*EventDelegateFeederConsent)(nil), "nibiru.oracle.v1.EventDelegateFeederConsent") proto.RegisterType((*EventAggregateVote)(nil), "nibiru.oracle.v1.EventAggregateVote") proto.RegisterType((*EventAggregatePrevote)(nil), "nibiru.oracle.v1.EventAggregatePrevote") + proto.RegisterType((*EventValidatorPerformance)(nil), "nibiru.oracle.v1.EventValidatorPerformance") } func init() { proto.RegisterFile("nibiru/oracle/v1/event.proto", fileDescriptor_94ec441b793fc0ea) } var fileDescriptor_94ec441b793fc0ea = []byte{ - // 407 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0xcd, 0x8e, 0xd3, 0x30, - 0x14, 0x85, 0x63, 0x0a, 0x95, 0xc6, 0xc3, 0x62, 0x64, 0x01, 0xaa, 0xa2, 0x92, 0x0e, 0x41, 0x42, - 0x5d, 0x80, 0xad, 0x81, 0x27, 0xa0, 0xd3, 0x99, 0xdd, 0xa0, 0x51, 0xc4, 0x8f, 0xc4, 0x06, 0xb9, - 0xc9, 0xc5, 0xb5, 0x48, 0x6c, 0xcb, 0x76, 0xa3, 0xe1, 0x29, 0xe0, 0x1d, 0xd8, 0xf1, 0x24, 0xb3, - 0xec, 0x12, 0xb1, 0x28, 0xa8, 0x7d, 0x11, 0x14, 0x27, 0xe5, 0xaf, 0xbb, 0xae, 0x72, 0x73, 0xce, - 0xf5, 0xc9, 0x17, 0xdf, 0x8b, 0x87, 0x4a, 0xce, 0xa4, 0x5d, 0x30, 0x6d, 0x79, 0x5e, 0x02, 0xab, - 0x4f, 0x18, 0xd4, 0xa0, 0x3c, 0x35, 0x56, 0x7b, 0x4d, 0x8e, 0x5a, 0x97, 0xb6, 0x2e, 0xad, 0x4f, - 0xe2, 0xfb, 0x3b, 0xfd, 0x9d, 0x17, 0x0e, 0xc4, 0x77, 0x84, 0x16, 0x3a, 0x94, 0xac, 0xa9, 0x3a, - 0x75, 0x28, 0xb4, 0x16, 0x25, 0x30, 0x6e, 0x24, 0xe3, 0x4a, 0x69, 0xcf, 0xbd, 0xd4, 0xca, 0xb5, - 0x6e, 0xfa, 0x09, 0xe1, 0xa3, 0xb3, 0xe6, 0xa3, 0x97, 0x56, 0xe6, 0xf0, 0xca, 0x14, 0xdc, 0x03, - 0x21, 0xf8, 0xa6, 0xe1, 0xd2, 0x0e, 0xd0, 0x31, 0x1a, 0x1f, 0x64, 0xa1, 0x26, 0x53, 0x7c, 0xcb, - 0x34, 0x2d, 0x83, 0x1b, 0x8d, 0x38, 0xa1, 0xd7, 0xab, 0x51, 0xf4, 0x7d, 0x35, 0x7a, 0x24, 0xa4, - 0x9f, 0x2f, 0x66, 0x34, 0xd7, 0x15, 0xcb, 0xb5, 0xab, 0xb4, 0xeb, 0x1e, 0x4f, 0x5c, 0xf1, 0x81, - 0xf9, 0x8f, 0x06, 0x1c, 0x9d, 0x42, 0x9e, 0xb5, 0x87, 0xc9, 0x03, 0x7c, 0xdb, 0xcb, 0x0a, 0x9c, - 0xe7, 0x95, 0x79, 0x57, 0xb9, 0x41, 0xef, 0x18, 0x8d, 0x7b, 0xd9, 0xe1, 0x6f, 0xed, 0xc2, 0xa5, - 0x19, 0x8e, 0x03, 0xd0, 0x14, 0x4a, 0x10, 0xdc, 0xc3, 0x39, 0x40, 0x01, 0xf6, 0x54, 0x2b, 0x07, - 0xca, 0x93, 0x21, 0x3e, 0xa8, 0x79, 0x29, 0x0b, 0xee, 0xf5, 0x96, 0xef, 0x8f, 0x40, 0xee, 0xe1, - 0xfe, 0xfb, 0xd0, 0xde, 0x52, 0x66, 0xdd, 0x5b, 0xfa, 0x05, 0x61, 0x12, 0x42, 0x9f, 0x0b, 0x61, - 0x43, 0xea, 0x6b, 0xed, 0x61, 0xbf, 0x30, 0xf2, 0x06, 0xf7, 0xc3, 0xcf, 0x34, 0xf4, 0xbd, 0xf1, - 0xe1, 0xd3, 0x87, 0xf4, 0xff, 0x41, 0xd1, 0xb3, 0xab, 0x7c, 0xce, 0x95, 0x80, 0x8c, 0x7b, 0x78, - 0xb9, 0x30, 0x25, 0x4c, 0xe2, 0xe6, 0xbe, 0xbe, 0xfe, 0x18, 0x91, 0x1d, 0xcb, 0x65, 0x5d, 0x5c, - 0x7a, 0x81, 0xef, 0xfe, 0x0b, 0x79, 0x69, 0xa1, 0xde, 0x9b, 0x73, 0x72, 0x7e, 0xbd, 0x4e, 0xd0, - 0x72, 0x9d, 0xa0, 0x9f, 0xeb, 0x04, 0x7d, 0xde, 0x24, 0xd1, 0x72, 0x93, 0x44, 0xdf, 0x36, 0x49, - 0xf4, 0xf6, 0xf1, 0x5f, 0x43, 0x7b, 0x11, 0xd8, 0x4f, 0xe7, 0x5c, 0x2a, 0xd6, 0xad, 0xd7, 0xd5, - 0x76, 0xc1, 0xc2, 0xf8, 0x66, 0xfd, 0xb0, 0x29, 0xcf, 0x7e, 0x05, 0x00, 0x00, 0xff, 0xff, 0x3b, - 0xea, 0x2b, 0x3c, 0xae, 0x02, 0x00, 0x00, + // 513 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0xcb, 0x6e, 0x13, 0x31, + 0x14, 0xcd, 0x90, 0x36, 0x22, 0x4e, 0x90, 0x2a, 0x0b, 0x50, 0x08, 0xe9, 0xa4, 0x4d, 0x25, 0x94, + 0x0d, 0x63, 0xb5, 0x7c, 0x01, 0x49, 0x8a, 0xc4, 0xa2, 0x28, 0x1a, 0x41, 0x2b, 0xb1, 0x89, 0x9c, + 0xc9, 0xad, 0x63, 0x91, 0xb1, 0x47, 0xb6, 0x33, 0x53, 0xbe, 0x02, 0xfe, 0x81, 0x1d, 0x5f, 0xd2, + 0x65, 0x97, 0x88, 0x45, 0x41, 0xc9, 0x8f, 0x20, 0x7b, 0x9c, 0xf2, 0xe8, 0x02, 0xa9, 0xab, 0xb9, + 0x3e, 0xe7, 0xf8, 0xcc, 0x99, 0xb9, 0xf7, 0xa2, 0x8e, 0xe0, 0x53, 0xae, 0x96, 0x44, 0x2a, 0x9a, + 0x2c, 0x80, 0xe4, 0x87, 0x04, 0x72, 0x10, 0x26, 0xca, 0x94, 0x34, 0x12, 0xef, 0x94, 0x6c, 0x54, + 0xb2, 0x51, 0x7e, 0xd8, 0xde, 0xbd, 0xa5, 0xf7, 0x9c, 0xbb, 0xd0, 0x7e, 0xc8, 0x24, 0x93, 0xae, + 0x24, 0xb6, 0xf2, 0x68, 0x87, 0x49, 0xc9, 0x16, 0x40, 0x68, 0xc6, 0x09, 0x15, 0x42, 0x1a, 0x6a, + 0xb8, 0x14, 0xba, 0x64, 0x7b, 0x9f, 0x02, 0xb4, 0x73, 0x6c, 0x5f, 0x3a, 0x56, 0x3c, 0x81, 0x77, + 0xd9, 0x8c, 0x1a, 0xc0, 0x18, 0x6d, 0x65, 0x94, 0xab, 0x56, 0xb0, 0x17, 0xf4, 0xeb, 0xb1, 0xab, + 0xf1, 0x08, 0x6d, 0x67, 0x56, 0xd2, 0xba, 0x67, 0xc1, 0x41, 0x74, 0x79, 0xdd, 0xad, 0x7c, 0xbf, + 0xee, 0x3e, 0x63, 0xdc, 0xcc, 0x97, 0xd3, 0x28, 0x91, 0x29, 0x49, 0xa4, 0x4e, 0xa5, 0xf6, 0x8f, + 0xe7, 0x7a, 0xf6, 0x81, 0x98, 0x8f, 0x19, 0xe8, 0x68, 0x04, 0x49, 0x5c, 0x5e, 0xc6, 0xfb, 0xa8, + 0x69, 0x78, 0x0a, 0xda, 0xd0, 0x34, 0x9b, 0xa4, 0xba, 0x55, 0xdd, 0x0b, 0xfa, 0xd5, 0xb8, 0x71, + 0x83, 0x9d, 0xe8, 0x5e, 0x8c, 0xda, 0x2e, 0xd0, 0x08, 0x16, 0xc0, 0xa8, 0x81, 0x57, 0x00, 0x33, + 0x50, 0x43, 0x29, 0x34, 0x08, 0x83, 0x3b, 0xa8, 0x9e, 0xd3, 0x05, 0x9f, 0x51, 0x23, 0x37, 0xf9, + 0x7e, 0x03, 0xf8, 0x31, 0xaa, 0x9d, 0x3b, 0x79, 0x99, 0x32, 0xf6, 0xa7, 0xde, 0x97, 0x00, 0x61, + 0x67, 0xfa, 0x92, 0x31, 0xe5, 0x5c, 0x4f, 0xa5, 0x81, 0xbb, 0x99, 0xe1, 0x33, 0x54, 0x73, 0x1f, + 0x63, 0xd3, 0x57, 0xfb, 0x8d, 0xa3, 0x83, 0xe8, 0xdf, 0x46, 0x45, 0xc7, 0x17, 0xc9, 0x9c, 0x0a, + 0x06, 0x31, 0x35, 0xf0, 0x76, 0x99, 0x2d, 0x60, 0xd0, 0xb6, 0xff, 0xeb, 0xeb, 0x8f, 0x2e, 0xbe, + 0x45, 0xe9, 0xd8, 0xdb, 0xf5, 0x4e, 0xd0, 0xa3, 0xbf, 0x43, 0x8e, 0x15, 0xe4, 0x77, 0xce, 0xd9, + 0x5b, 0x05, 0xe8, 0x89, 0xf3, 0x3b, 0xdd, 0x48, 0xc7, 0xa0, 0xce, 0xa5, 0x4a, 0xa9, 0x48, 0xfe, + 0xe7, 0xb9, 0x8f, 0x9a, 0xb9, 0x34, 0x5c, 0xb0, 0x49, 0x26, 0x0b, 0xef, 0x5c, 0x8d, 0x1b, 0x25, + 0x36, 0xb6, 0x10, 0x3e, 0x40, 0x0f, 0x14, 0x14, 0x54, 0xcd, 0x26, 0x05, 0x70, 0x36, 0x37, 0xbe, + 0x97, 0xcd, 0x12, 0x3c, 0x73, 0x18, 0x7e, 0x8a, 0xea, 0x05, 0x17, 0x93, 0x44, 0x2e, 0x85, 0x69, + 0x6d, 0x39, 0xc1, 0xfd, 0x82, 0x8b, 0xa1, 0x3d, 0x5b, 0x07, 0x3a, 0xd5, 0x86, 0xde, 0x08, 0xb6, + 0x4b, 0x07, 0x0f, 0x96, 0xa2, 0x5d, 0x84, 0x52, 0xae, 0xb5, 0x57, 0xd4, 0x9c, 0xa2, 0x6e, 0x11, + 0x47, 0x0f, 0x5e, 0x5f, 0xae, 0xc2, 0xe0, 0x6a, 0x15, 0x06, 0x3f, 0x57, 0x61, 0xf0, 0x79, 0x1d, + 0x56, 0xae, 0xd6, 0x61, 0xe5, 0xdb, 0x3a, 0xac, 0xbc, 0x27, 0x7f, 0x4c, 0xe6, 0x1b, 0xd7, 0xa0, + 0xe1, 0x9c, 0x72, 0x41, 0xfc, 0x0e, 0xe5, 0x47, 0xe4, 0x62, 0xb3, 0x48, 0x6e, 0x4c, 0xa7, 0x35, + 0xb7, 0x11, 0x2f, 0x7e, 0x05, 0x00, 0x00, 0xff, 0xff, 0x14, 0xe0, 0x93, 0xd3, 0x96, 0x03, 0x00, + 0x00, } func (m *EventPriceUpdate) Marshal() (dAtA []byte, err error) { @@ -465,6 +564,61 @@ func (m *EventAggregatePrevote) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *EventValidatorPerformance) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventValidatorPerformance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventValidatorPerformance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MissCount != 0 { + i = encodeVarintEvent(dAtA, i, uint64(m.MissCount)) + i-- + dAtA[i] = 0x30 + } + if m.AbstainCount != 0 { + i = encodeVarintEvent(dAtA, i, uint64(m.AbstainCount)) + i-- + dAtA[i] = 0x28 + } + if m.WinCount != 0 { + i = encodeVarintEvent(dAtA, i, uint64(m.WinCount)) + i-- + dAtA[i] = 0x20 + } + if m.RewardWeight != 0 { + i = encodeVarintEvent(dAtA, i, uint64(m.RewardWeight)) + i-- + dAtA[i] = 0x18 + } + if m.VotingPower != 0 { + i = encodeVarintEvent(dAtA, i, uint64(m.VotingPower)) + i-- + dAtA[i] = 0x10 + } + if len(m.Validator) > 0 { + i -= len(m.Validator) + copy(dAtA[i:], m.Validator) + i = encodeVarintEvent(dAtA, i, uint64(len(m.Validator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintEvent(dAtA []byte, offset int, v uint64) int { offset -= sovEvent(v) base := offset @@ -551,6 +705,34 @@ func (m *EventAggregatePrevote) Size() (n int) { return n } +func (m *EventValidatorPerformance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Validator) + if l > 0 { + n += 1 + l + sovEvent(uint64(l)) + } + if m.VotingPower != 0 { + n += 1 + sovEvent(uint64(m.VotingPower)) + } + if m.RewardWeight != 0 { + n += 1 + sovEvent(uint64(m.RewardWeight)) + } + if m.WinCount != 0 { + n += 1 + sovEvent(uint64(m.WinCount)) + } + if m.AbstainCount != 0 { + n += 1 + sovEvent(uint64(m.AbstainCount)) + } + if m.MissCount != 0 { + n += 1 + sovEvent(uint64(m.MissCount)) + } + return n +} + func sovEvent(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1068,6 +1250,183 @@ func (m *EventAggregatePrevote) Unmarshal(dAtA []byte) error { } return nil } +func (m *EventValidatorPerformance) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventValidatorPerformance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventValidatorPerformance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvent + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Validator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VotingPower", wireType) + } + m.VotingPower = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VotingPower |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RewardWeight", wireType) + } + m.RewardWeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RewardWeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WinCount", wireType) + } + m.WinCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.WinCount |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AbstainCount", wireType) + } + m.AbstainCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AbstainCount |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MissCount", wireType) + } + m.MissCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MissCount |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvent(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvent + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipEvent(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/oracle/types/expected_keeper.go b/x/oracle/types/expected_keeper.go index 98ea85cb1..c50f4b987 100644 --- a/x/oracle/types/expected_keeper.go +++ b/x/oracle/types/expected_keeper.go @@ -46,3 +46,10 @@ type BankKeeper interface { // only used for simulation SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins } + +type SudoKeeper interface { + // CheckPermissions Checks if a contract is contained within the set of sudo + // contracts defined in the x/sudo module. These smart contracts are able to + // execute certain permissioned functions. + CheckPermissions(contract sdk.AccAddress, ctx sdk.Context) error +} diff --git a/x/oracle/types/genesis.go b/x/oracle/types/genesis.go index 18aa327ed..dd5f3f842 100644 --- a/x/oracle/types/genesis.go +++ b/x/oracle/types/genesis.go @@ -5,7 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/NibiruChain/nibiru/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/asset" ) // NewGenesisState creates a new GenesisState object diff --git a/x/oracle/types/genesis.pb.go b/x/oracle/types/genesis.pb.go index 6f7706c1e..d6f7a6014 100644 --- a/x/oracle/types/genesis.pb.go +++ b/x/oracle/types/genesis.pb.go @@ -5,7 +5,7 @@ package types import ( fmt "fmt" - github_com_NibiruChain_nibiru_x_common_asset "github.com/NibiruChain/nibiru/x/common/asset" + github_com_NibiruChain_nibiru_v2_x_common_asset "github.com/NibiruChain/nibiru/v2/x/common/asset" _ "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" @@ -27,14 +27,14 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState defines the oracle module's genesis state. type GenesisState struct { - Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` - FeederDelegations []FeederDelegation `protobuf:"bytes,2,rep,name=feeder_delegations,json=feederDelegations,proto3" json:"feeder_delegations"` - ExchangeRates ExchangeRateTuples `protobuf:"bytes,3,rep,name=exchange_rates,json=exchangeRates,proto3,castrepeated=ExchangeRateTuples" json:"exchange_rates"` - MissCounters []MissCounter `protobuf:"bytes,4,rep,name=miss_counters,json=missCounters,proto3" json:"miss_counters"` - AggregateExchangeRatePrevotes []AggregateExchangeRatePrevote `protobuf:"bytes,5,rep,name=aggregate_exchange_rate_prevotes,json=aggregateExchangeRatePrevotes,proto3" json:"aggregate_exchange_rate_prevotes"` - AggregateExchangeRateVotes []AggregateExchangeRateVote `protobuf:"bytes,6,rep,name=aggregate_exchange_rate_votes,json=aggregateExchangeRateVotes,proto3" json:"aggregate_exchange_rate_votes"` - Pairs []github_com_NibiruChain_nibiru_x_common_asset.Pair `protobuf:"bytes,7,rep,name=pairs,proto3,customtype=github.com/NibiruChain/nibiru/x/common/asset.Pair" json:"pairs"` - Rewards []Rewards `protobuf:"bytes,8,rep,name=rewards,proto3" json:"rewards"` + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + FeederDelegations []FeederDelegation `protobuf:"bytes,2,rep,name=feeder_delegations,json=feederDelegations,proto3" json:"feeder_delegations"` + ExchangeRates ExchangeRateTuples `protobuf:"bytes,3,rep,name=exchange_rates,json=exchangeRates,proto3,castrepeated=ExchangeRateTuples" json:"exchange_rates"` + MissCounters []MissCounter `protobuf:"bytes,4,rep,name=miss_counters,json=missCounters,proto3" json:"miss_counters"` + AggregateExchangeRatePrevotes []AggregateExchangeRatePrevote `protobuf:"bytes,5,rep,name=aggregate_exchange_rate_prevotes,json=aggregateExchangeRatePrevotes,proto3" json:"aggregate_exchange_rate_prevotes"` + AggregateExchangeRateVotes []AggregateExchangeRateVote `protobuf:"bytes,6,rep,name=aggregate_exchange_rate_votes,json=aggregateExchangeRateVotes,proto3" json:"aggregate_exchange_rate_votes"` + Pairs []github_com_NibiruChain_nibiru_v2_x_common_asset.Pair `protobuf:"bytes,7,rep,name=pairs,proto3,customtype=github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" json:"pairs"` + Rewards []Rewards `protobuf:"bytes,8,rep,name=rewards,proto3" json:"rewards"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -237,42 +237,42 @@ func init() { func init() { proto.RegisterFile("nibiru/oracle/v1/genesis.proto", fileDescriptor_d88ebb2fa2659942) } var fileDescriptor_d88ebb2fa2659942 = []byte{ - // 546 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x94, 0x5f, 0x6b, 0x13, 0x4d, - 0x14, 0xc6, 0xb3, 0xfd, 0xfb, 0x76, 0xd2, 0x94, 0x74, 0x78, 0x2f, 0xd6, 0x40, 0x36, 0x31, 0x22, - 0x04, 0x2a, 0xbb, 0xa4, 0x82, 0xd0, 0xcb, 0xa6, 0x5a, 0xbd, 0x51, 0xc3, 0x2a, 0x0a, 0x82, 0x84, - 0xc9, 0xee, 0xc9, 0x66, 0x20, 0xbb, 0xb3, 0xcc, 0x99, 0xc4, 0x7a, 0xe1, 0x77, 0xf0, 0x73, 0xf8, - 0x49, 0x7a, 0x59, 0xbc, 0x12, 0x2f, 0xaa, 0x24, 0x5f, 0x44, 0x76, 0x66, 0xdb, 0xc4, 0x6c, 0xab, - 0xde, 0x85, 0xf3, 0xfc, 0xce, 0xf3, 0x9c, 0x09, 0x4f, 0x42, 0x9c, 0x84, 0x0f, 0xb8, 0x9c, 0x78, - 0x42, 0xb2, 0x60, 0x0c, 0xde, 0xb4, 0xe3, 0x45, 0x90, 0x00, 0x72, 0x74, 0x53, 0x29, 0x94, 0xa0, - 0x55, 0xa3, 0xbb, 0x46, 0x77, 0xa7, 0x9d, 0xda, 0xff, 0x91, 0x88, 0x84, 0x16, 0xbd, 0xec, 0x93, - 0xe1, 0x6a, 0xf5, 0x82, 0x4f, 0xbe, 0x61, 0x64, 0x27, 0x10, 0x18, 0x0b, 0xf4, 0x06, 0x0c, 0x33, - 0x71, 0x00, 0x8a, 0x75, 0xbc, 0x40, 0xf0, 0xc4, 0xe8, 0xad, 0xaf, 0x9b, 0x64, 0xf7, 0xa9, 0x09, - 0x7e, 0xa5, 0x98, 0x02, 0xfa, 0x88, 0x6c, 0xa5, 0x4c, 0xb2, 0x18, 0x6d, 0xab, 0x69, 0xb5, 0xcb, - 0x87, 0xb6, 0xbb, 0x7a, 0x88, 0xdb, 0xd3, 0x7a, 0x77, 0xe3, 0xfc, 0xb2, 0x51, 0xf2, 0x73, 0x9a, - 0xbe, 0x25, 0x74, 0x08, 0x10, 0x82, 0xec, 0x87, 0x30, 0x86, 0x88, 0x29, 0x2e, 0x12, 0xb4, 0xd7, - 0x9a, 0xeb, 0xed, 0xf2, 0x61, 0xab, 0xe8, 0x71, 0xaa, 0xd9, 0xc7, 0xd7, 0x68, 0xee, 0xb6, 0x3f, - 0x5c, 0x99, 0x23, 0x1d, 0x92, 0x3d, 0x38, 0x0b, 0x46, 0x2c, 0x89, 0xa0, 0x2f, 0x99, 0x02, 0xb4, - 0xd7, 0xb5, 0xe9, 0xbd, 0xa2, 0xe9, 0x93, 0x9c, 0xf3, 0x99, 0x82, 0xd7, 0x93, 0x74, 0x0c, 0xdd, - 0x5a, 0xe6, 0xfa, 0xe5, 0x47, 0x83, 0x16, 0x24, 0xf4, 0x2b, 0xb0, 0x34, 0x43, 0xfa, 0x8c, 0x54, - 0x62, 0x8e, 0xd8, 0x0f, 0xc4, 0x24, 0x51, 0x20, 0xd1, 0xde, 0xd0, 0x31, 0xf5, 0x62, 0xcc, 0x73, - 0x8e, 0x78, 0x62, 0xa8, 0xfc, 0xec, 0xdd, 0x78, 0x31, 0x42, 0xfa, 0x89, 0x34, 0x59, 0x14, 0xc9, - 0xec, 0x05, 0xd0, 0xff, 0xed, 0xf6, 0x7e, 0x2a, 0x61, 0x2a, 0xb2, 0x37, 0x6c, 0x6a, 0x73, 0xb7, - 0x68, 0x7e, 0x7c, 0xb5, 0xb9, 0x7c, 0x71, 0xcf, 0xac, 0xe5, 0x69, 0x75, 0xf6, 0x07, 0x06, 0xa9, - 0x22, 0xf5, 0xdb, 0xe2, 0x4d, 0xf6, 0x96, 0xce, 0x3e, 0xf8, 0xc7, 0xec, 0x37, 0x8b, 0xe0, 0x1a, - 0xbb, 0x0d, 0x40, 0xfa, 0x92, 0x6c, 0xa6, 0x8c, 0x4b, 0xb4, 0xb7, 0x9b, 0xeb, 0xed, 0x9d, 0xee, - 0x51, 0xb6, 0xf0, 0xfd, 0xb2, 0xd1, 0x89, 0xb8, 0x1a, 0x4d, 0x06, 0x6e, 0x20, 0x62, 0xef, 0x85, - 0xce, 0x3b, 0x19, 0x31, 0x9e, 0x78, 0x79, 0x6b, 0xcf, 0xbc, 0x40, 0xc4, 0xb1, 0x48, 0x3c, 0x86, - 0x08, 0xca, 0xed, 0x31, 0x2e, 0x7d, 0xe3, 0x43, 0x8f, 0xc8, 0xb6, 0x84, 0x0f, 0x4c, 0x86, 0x68, - 0xff, 0xa7, 0x0f, 0xbe, 0x53, 0x3c, 0xd8, 0x37, 0x40, 0x7e, 0xde, 0x15, 0xdf, 0x1a, 0x92, 0xea, - 0x6a, 0xbf, 0xe8, 0x7d, 0xb2, 0x97, 0xf7, 0x93, 0x85, 0xa1, 0x04, 0x34, 0xfd, 0xde, 0xf1, 0x2b, - 0x66, 0x7a, 0x6c, 0x86, 0xf4, 0x80, 0xec, 0x4f, 0xd9, 0x98, 0x87, 0x4c, 0x89, 0x05, 0xb9, 0xa6, - 0xc9, 0xea, 0xb5, 0x90, 0xc3, 0xad, 0xf7, 0xa4, 0xbc, 0xd4, 0x85, 0x9b, 0x77, 0xad, 0x9b, 0x77, - 0xe9, 0x5d, 0xb2, 0xbb, 0x5c, 0x37, 0x9d, 0xb1, 0xe1, 0x97, 0x97, 0x8a, 0xd4, 0x3d, 0x3d, 0x9f, - 0x39, 0xd6, 0xc5, 0xcc, 0xb1, 0x7e, 0xce, 0x1c, 0xeb, 0xf3, 0xdc, 0x29, 0x5d, 0xcc, 0x9d, 0xd2, - 0xb7, 0xb9, 0x53, 0x7a, 0xf7, 0xe0, 0x6f, 0xdf, 0x6a, 0xfe, 0x6f, 0xa0, 0x3e, 0xa6, 0x80, 0x83, - 0x2d, 0xfd, 0x53, 0x7f, 0xf8, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x56, 0x94, 0xe7, 0x7b, 0x73, 0x04, - 0x00, 0x00, + // 549 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x94, 0x4f, 0x6b, 0x13, 0x41, + 0x18, 0xc6, 0xb3, 0x6d, 0x93, 0xda, 0x49, 0x52, 0xd2, 0xc1, 0xc3, 0x1a, 0xc8, 0x26, 0x46, 0x84, + 0x40, 0x61, 0x87, 0x44, 0x11, 0x04, 0x2f, 0x4d, 0xfd, 0x7b, 0x50, 0xc2, 0x2a, 0x0a, 0x82, 0x84, + 0xc9, 0xee, 0x64, 0x33, 0x90, 0xdd, 0x59, 0xe6, 0x9d, 0xc4, 0x7a, 0xf0, 0x3b, 0xf8, 0x39, 0xfc, + 0x24, 0x3d, 0xf6, 0x22, 0x88, 0x87, 0x2a, 0xc9, 0x17, 0x91, 0x9d, 0xd9, 0x36, 0x31, 0xdb, 0x4a, + 0x6f, 0xe1, 0x7d, 0x7e, 0xef, 0xf3, 0xbc, 0x13, 0x9e, 0x04, 0x39, 0x31, 0x1f, 0x71, 0x39, 0x23, + 0x42, 0x52, 0x7f, 0xca, 0xc8, 0xbc, 0x4b, 0x42, 0x16, 0x33, 0xe0, 0xe0, 0x26, 0x52, 0x28, 0x81, + 0x6b, 0x46, 0x77, 0x8d, 0xee, 0xce, 0xbb, 0xf5, 0xdb, 0xa1, 0x08, 0x85, 0x16, 0x49, 0xfa, 0xc9, + 0x70, 0xf5, 0x46, 0xce, 0x27, 0xdb, 0x30, 0xb2, 0xe3, 0x0b, 0x88, 0x04, 0x90, 0x11, 0x85, 0x54, + 0x1c, 0x31, 0x45, 0xbb, 0xc4, 0x17, 0x3c, 0x36, 0x7a, 0xfb, 0x47, 0x11, 0x55, 0x5e, 0x98, 0xe0, + 0xb7, 0x8a, 0x2a, 0x86, 0x1f, 0xa1, 0x52, 0x42, 0x25, 0x8d, 0xc0, 0xb6, 0x5a, 0x56, 0xa7, 0xdc, + 0xb3, 0xdd, 0xcd, 0x43, 0xdc, 0x81, 0xd6, 0xfb, 0x3b, 0xa7, 0xe7, 0xcd, 0x82, 0x97, 0xd1, 0xf8, + 0x03, 0xc2, 0x63, 0xc6, 0x02, 0x26, 0x87, 0x01, 0x9b, 0xb2, 0x90, 0x2a, 0x2e, 0x62, 0xb0, 0xb7, + 0x5a, 0xdb, 0x9d, 0x72, 0xaf, 0x9d, 0xf7, 0x78, 0xae, 0xd9, 0xa7, 0x97, 0x68, 0xe6, 0x76, 0x30, + 0xde, 0x98, 0x03, 0x1e, 0xa3, 0x7d, 0x76, 0xe2, 0x4f, 0x68, 0x1c, 0xb2, 0xa1, 0xa4, 0x8a, 0x81, + 0xbd, 0xad, 0x4d, 0xef, 0xe5, 0x4d, 0x9f, 0x65, 0x9c, 0x47, 0x15, 0x7b, 0x37, 0x4b, 0xa6, 0xac, + 0x5f, 0x4f, 0x5d, 0xbf, 0xff, 0x6e, 0xe2, 0x9c, 0x04, 0x5e, 0x95, 0xad, 0xcd, 0x00, 0xbf, 0x44, + 0xd5, 0x88, 0x03, 0x0c, 0x7d, 0x31, 0x8b, 0x15, 0x93, 0x60, 0xef, 0xe8, 0x98, 0x46, 0x3e, 0xe6, + 0x35, 0x07, 0x38, 0x36, 0x54, 0x76, 0x76, 0x25, 0x5a, 0x8d, 0x00, 0x7f, 0x45, 0x2d, 0x1a, 0x86, + 0x32, 0x7d, 0x01, 0x1b, 0xfe, 0x73, 0xfb, 0x30, 0x91, 0x6c, 0x2e, 0xd2, 0x37, 0x14, 0xb5, 0xb9, + 0x9b, 0x37, 0x3f, 0xba, 0xd8, 0x5c, 0xbf, 0x78, 0x60, 0xd6, 0xb2, 0xb4, 0x06, 0xfd, 0x0f, 0x03, + 0x58, 0xa1, 0xc6, 0x75, 0xf1, 0x26, 0xbb, 0xa4, 0xb3, 0x0f, 0x6f, 0x98, 0xfd, 0x7e, 0x15, 0x5c, + 0xa7, 0xd7, 0x01, 0x80, 0x3d, 0x54, 0x4c, 0x28, 0x97, 0x60, 0xef, 0xb6, 0xb6, 0x3b, 0x7b, 0xfd, + 0x27, 0xe9, 0xc2, 0xaf, 0xf3, 0xe6, 0xc3, 0x90, 0xab, 0xc9, 0x6c, 0xe4, 0xfa, 0x22, 0x22, 0x6f, + 0x74, 0xde, 0xf1, 0x84, 0xf2, 0x98, 0x64, 0xad, 0x9d, 0xf7, 0xc8, 0x09, 0xf1, 0x45, 0x14, 0x89, + 0x98, 0x50, 0x00, 0xa6, 0xdc, 0x01, 0xe5, 0xd2, 0x33, 0x56, 0xf8, 0x31, 0xda, 0x95, 0xec, 0x33, + 0x95, 0x01, 0xd8, 0xb7, 0xf4, 0xcd, 0x77, 0xf2, 0x37, 0x7b, 0x06, 0xc8, 0x2e, 0xbc, 0xe0, 0xdb, + 0x63, 0x54, 0xdb, 0xac, 0x18, 0xbe, 0x8f, 0xf6, 0xb3, 0x8a, 0xd2, 0x20, 0x90, 0x0c, 0x4c, 0xc5, + 0xf7, 0xbc, 0xaa, 0x99, 0x1e, 0x99, 0x21, 0x3e, 0x44, 0x07, 0x73, 0x3a, 0xe5, 0x01, 0x55, 0x62, + 0x45, 0x6e, 0x69, 0xb2, 0x76, 0x29, 0x64, 0x70, 0xfb, 0x13, 0x2a, 0xaf, 0xd5, 0xe1, 0xea, 0x5d, + 0xeb, 0xea, 0x5d, 0x7c, 0x17, 0x55, 0xd6, 0x1b, 0xa7, 0x33, 0x76, 0xbc, 0xf2, 0x5a, 0x97, 0xfa, + 0xaf, 0x4e, 0x17, 0x8e, 0x75, 0xb6, 0x70, 0xac, 0x3f, 0x0b, 0xc7, 0xfa, 0xb6, 0x74, 0x0a, 0x67, + 0x4b, 0xa7, 0xf0, 0x73, 0xe9, 0x14, 0x3e, 0x92, 0x1b, 0x7c, 0xb1, 0xd9, 0x7f, 0x82, 0xfa, 0x92, + 0x30, 0x18, 0x95, 0xf4, 0x0f, 0xfe, 0xc1, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x90, 0xe5, 0x08, + 0xe6, 0x79, 0x04, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -843,7 +843,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - var v github_com_NibiruChain_nibiru_x_common_asset.Pair + var v github_com_NibiruChain_nibiru_v2_x_common_asset.Pair m.Pairs = append(m.Pairs, v) if err := m.Pairs[len(m.Pairs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err diff --git a/x/oracle/types/genesis_test.go b/x/oracle/types/genesis_test.go index deedc85e7..147e8a0af 100644 --- a/x/oracle/types/genesis_test.go +++ b/x/oracle/types/genesis_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestGenesisValidation(t *testing.T) { @@ -19,7 +19,7 @@ func TestGenesisValidation(t *testing.T) { } func TestGetGenesisStateFromAppState(t *testing.T) { - cdc := app.MakeEncodingConfig().Marshaler + cdc := app.MakeEncodingConfig().Codec appState := make(map[string]json.RawMessage) defaultGenesisState := types.DefaultGenesisState() diff --git a/x/oracle/types/hash.go b/x/oracle/types/hash.go index b9d816ecc..a828a6414 100644 --- a/x/oracle/types/hash.go +++ b/x/oracle/types/hash.go @@ -99,7 +99,7 @@ func (h AggregateVoteHash) MarshalJSON() ([]byte, error) { } // MarshalYAML marshals to YAML using Bech32. -func (h AggregateVoteHash) MarshalYAML() (interface{}, error) { +func (h AggregateVoteHash) MarshalYAML() (any, error) { return h.String(), nil } diff --git a/x/oracle/types/hash_test.go b/x/oracle/types/hash_test.go index 86b47fb50..752d45de1 100644 --- a/x/oracle/types/hash_test.go +++ b/x/oracle/types/hash_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/x/oracle/types/msgs.go b/x/oracle/types/msgs.go index 8f07aba08..317566226 100644 --- a/x/oracle/types/msgs.go +++ b/x/oracle/types/msgs.go @@ -19,9 +19,9 @@ var ( // oracle message types const ( TypeMsgDelegateFeedConsent = "delegate_feeder" - TypeMsgEditOracleParams = "edit_oracle_params" TypeMsgAggregateExchangeRatePrevote = "aggregate_exchange_rate_prevote" TypeMsgAggregateExchangeRateVote = "aggregate_exchange_rate_vote" + TypeMsgEditOracleParams = "edit_oracle_params" ) //------------------------------------------------- @@ -151,6 +151,8 @@ func (msg MsgAggregateExchangeRateVote) ValidateBasic() error { return nil } +// ------------------------ MsgDelegateFeedConsent ------------------------ + // NewMsgDelegateFeedConsent creates a MsgDelegateFeedConsent instance func NewMsgDelegateFeedConsent(operatorAddress sdk.ValAddress, feederAddress sdk.AccAddress) *MsgDelegateFeedConsent { return &MsgDelegateFeedConsent{ @@ -195,35 +197,26 @@ func (msg MsgDelegateFeedConsent) ValidateBasic() error { return nil } -func (msg MsgEditOracleParams) Route() string { return RouterKey } +// ------------------------ MsgEditOracleParams ------------------------ -func (msg MsgEditOracleParams) Type() string { return TypeMsgEditOracleParams } +func (m MsgEditOracleParams) Route() string { return RouterKey } +func (m MsgEditOracleParams) Type() string { return TypeMsgEditOracleParams } -// GetSignBytes implements sdk.Msg -func (msg MsgEditOracleParams) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) -} - -// GetSigners implements sdk.Msg -func (msg MsgEditOracleParams) GetSigners() []sdk.AccAddress { - sender, err := sdk.AccAddressFromBech32(msg.Sender) - if err != nil { - panic(err) +func (m MsgEditOracleParams) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { + return err } + return nil +} - return []sdk.AccAddress{sdk.AccAddress(sender)} +func (m MsgEditOracleParams) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m)) } -// ValidateBasic implements sdk.Msg -func (msg MsgEditOracleParams) ValidateBasic() error { - _, err := sdk.AccAddressFromBech32(msg.Sender) +func (m MsgEditOracleParams) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(m.Sender) if err != nil { - return sdkerrors.Wrapf(errors.ErrInvalidAddress, "Invalid sender address (%s)", err) - } - - if msg.Params == nil { - return sdkerrors.Wrap(errors.ErrInvalidRequest, "params cannot be nil") + panic(err) } - - return nil + return []sdk.AccAddress{signer} } diff --git a/x/oracle/types/msgs_test.go b/x/oracle/types/msgs_test.go index f85ebfd85..265eae0bb 100644 --- a/x/oracle/types/msgs_test.go +++ b/x/oracle/types/msgs_test.go @@ -3,8 +3,10 @@ package types_test import ( "testing" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/oracle/types" + "cosmossdk.io/math" + + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" "github.com/stretchr/testify/require" @@ -43,7 +45,7 @@ func TestMsgAggregateExchangeRatePrevote(t *testing.T) { sdk.AccAddress([]byte("addr1_______________")), } - exchangeRates := sdk.DecCoins{sdk.NewDecCoinFromDec(denoms.USDC, sdk.OneDec()), sdk.NewDecCoinFromDec(denoms.NUSD, sdk.NewDecWithPrec(32121, 1))} + exchangeRates := sdk.DecCoins{sdk.NewDecCoinFromDec(denoms.USDC, math.LegacyOneDec()), sdk.NewDecCoinFromDec(denoms.NUSD, math.LegacyNewDecWithPrec(32121, 1))} bz := types.GetAggregateVoteHash("1", exchangeRates.String(), sdk.ValAddress(addrs[0])) tests := []struct { @@ -76,22 +78,22 @@ func TestMsgAggregateExchangeRateVote(t *testing.T) { exchangeRates := types.ExchangeRateTuples{ { Pair: "FOO:USD", - ExchangeRate: sdk.MustNewDecFromStr("1.0"), + ExchangeRate: math.LegacyMustNewDecFromStr("1.0"), }, { Pair: "BAR:USD", - ExchangeRate: sdk.MustNewDecFromStr("1232.132"), + ExchangeRate: math.LegacyMustNewDecFromStr("1232.132"), }, } abstainExchangeRates := types.ExchangeRateTuples{ { Pair: "FOO:USD", - ExchangeRate: sdk.ZeroDec(), + ExchangeRate: math.LegacyZeroDec(), }, { Pair: "BAR:USD", - ExchangeRate: sdk.MustNewDecFromStr("1232.132"), + ExchangeRate: math.LegacyMustNewDecFromStr("1232.132"), }, } diff --git a/x/oracle/types/oracle.pb.go b/x/oracle/types/oracle.pb.go index a1e849d40..2585900fd 100644 --- a/x/oracle/types/oracle.pb.go +++ b/x/oracle/types/oracle.pb.go @@ -5,7 +5,7 @@ package types import ( fmt "fmt" - github_com_NibiruChain_nibiru_x_common_asset "github.com/NibiruChain/nibiru/x/common/asset" + github_com_NibiruChain_nibiru_v2_x_common_asset "github.com/NibiruChain/nibiru/v2/x/common/asset" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" @@ -48,7 +48,7 @@ type Params struct { RewardBand github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=reward_band,json=rewardBand,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"reward_band" yaml:"reward_band"` // The set of whitelisted markets, or asset pairs, for the module. // Ex. '["unibi:uusd","ubtc:uusd"]' - Whitelist []github_com_NibiruChain_nibiru_x_common_asset.Pair `protobuf:"bytes,4,rep,name=whitelist,proto3,customtype=github.com/NibiruChain/nibiru/x/common/asset.Pair" json:"whitelist,omitempty" yaml:"whitelist"` + Whitelist []github_com_NibiruChain_nibiru_v2_x_common_asset.Pair `protobuf:"bytes,4,rep,name=whitelist,proto3,customtype=github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" json:"whitelist,omitempty" yaml:"whitelist"` // SlashFraction returns the proportion of an oracle's stake that gets // slashed in the event of slashing. `SlashFraction` specifies the exact // penalty for failing a voting period. @@ -59,7 +59,8 @@ type Params struct { // synonymous with submitting fewer valid votes than `MinValidPerWindow`. SlashWindow uint64 `protobuf:"varint,6,opt,name=slash_window,json=slashWindow,proto3" json:"slash_window,omitempty" yaml:"slash_window"` MinValidPerWindow github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,7,opt,name=min_valid_per_window,json=minValidPerWindow,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"min_valid_per_window" yaml:"min_valid_per_window"` - // Amount of time to look back for TWAP calculations + // Amount of time to look back for TWAP calculations. + // Ex: "900.000000069s" corresponds to 900 seconds and 69 nanoseconds in JSON. TwapLookbackWindow time.Duration `protobuf:"bytes,8,opt,name=twap_lookback_window,json=twapLookbackWindow,proto3,stdduration" json:"twap_lookback_window,omitempty" yaml:"twap_lookback_window"` // The minimum number of voters (i.e. oracle validators) per pair for it to be // considered a passing ballot. Recommended at least 4. @@ -69,8 +70,9 @@ type Params struct { ExpirationBlocks uint64 `protobuf:"varint,11,opt,name=expiration_blocks,json=expirationBlocks,proto3" json:"expiration_blocks,omitempty" yaml:"expiration_blocks"` } -func (m *Params) Reset() { *m = Params{} } -func (*Params) ProtoMessage() {} +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} func (*Params) Descriptor() ([]byte, []int) { return fileDescriptor_43d45df86ea09ed4, []int{0} } @@ -146,8 +148,9 @@ type AggregateExchangeRatePrevote struct { SubmitBlock uint64 `protobuf:"varint,3,opt,name=submit_block,json=submitBlock,proto3" json:"submit_block,omitempty" yaml:"submit_block"` } -func (m *AggregateExchangeRatePrevote) Reset() { *m = AggregateExchangeRatePrevote{} } -func (*AggregateExchangeRatePrevote) ProtoMessage() {} +func (m *AggregateExchangeRatePrevote) Reset() { *m = AggregateExchangeRatePrevote{} } +func (m *AggregateExchangeRatePrevote) String() string { return proto.CompactTextString(m) } +func (*AggregateExchangeRatePrevote) ProtoMessage() {} func (*AggregateExchangeRatePrevote) Descriptor() ([]byte, []int) { return fileDescriptor_43d45df86ea09ed4, []int{1} } @@ -185,8 +188,9 @@ type AggregateExchangeRateVote struct { Voter string `protobuf:"bytes,2,opt,name=voter,proto3" json:"voter,omitempty" yaml:"voter"` } -func (m *AggregateExchangeRateVote) Reset() { *m = AggregateExchangeRateVote{} } -func (*AggregateExchangeRateVote) ProtoMessage() {} +func (m *AggregateExchangeRateVote) Reset() { *m = AggregateExchangeRateVote{} } +func (m *AggregateExchangeRateVote) String() string { return proto.CompactTextString(m) } +func (*AggregateExchangeRateVote) ProtoMessage() {} func (*AggregateExchangeRateVote) Descriptor() ([]byte, []int) { return fileDescriptor_43d45df86ea09ed4, []int{2} } @@ -219,12 +223,13 @@ var xxx_messageInfo_AggregateExchangeRateVote proto.InternalMessageInfo // ExchangeRateTuple - struct to store interpreted exchange rates data to store type ExchangeRateTuple struct { - Pair github_com_NibiruChain_nibiru_x_common_asset.Pair `protobuf:"bytes,1,opt,name=pair,proto3,customtype=github.com/NibiruChain/nibiru/x/common/asset.Pair" json:"pair" yaml:"pair"` - ExchangeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=exchange_rate,json=exchangeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"exchange_rate" yaml:"exchange_rate"` + Pair github_com_NibiruChain_nibiru_v2_x_common_asset.Pair `protobuf:"bytes,1,opt,name=pair,proto3,customtype=github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" json:"pair" yaml:"pair"` + ExchangeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=exchange_rate,json=exchangeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"exchange_rate" yaml:"exchange_rate"` } -func (m *ExchangeRateTuple) Reset() { *m = ExchangeRateTuple{} } -func (*ExchangeRateTuple) ProtoMessage() {} +func (m *ExchangeRateTuple) Reset() { *m = ExchangeRateTuple{} } +func (m *ExchangeRateTuple) String() string { return proto.CompactTextString(m) } +func (*ExchangeRateTuple) ProtoMessage() {} func (*ExchangeRateTuple) Descriptor() ([]byte, []int) { return fileDescriptor_43d45df86ea09ed4, []int{3} } @@ -255,23 +260,27 @@ func (m *ExchangeRateTuple) XXX_DiscardUnknown() { var xxx_messageInfo_ExchangeRateTuple proto.InternalMessageInfo -type DatedPrice struct { +type ExchangeRateAtBlock struct { ExchangeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=exchange_rate,json=exchangeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"exchange_rate" yaml:"exchange_rate"` CreatedBlock uint64 `protobuf:"varint,2,opt,name=created_block,json=createdBlock,proto3" json:"created_block,omitempty" yaml:"created_block"` + // Block timestamp for the block where the oracle came to consensus for this + // price. This timestamp is a conventional Unix millisecond time, i.e. the + // number of milliseconds elapsed since January 1, 1970 UTC. + BlockTimestampMs int64 `protobuf:"varint,3,opt,name=block_timestamp_ms,json=blockTimestampMs,proto3" json:"block_timestamp_ms,omitempty" yaml:"block_timestamp_ms"` } -func (m *DatedPrice) Reset() { *m = DatedPrice{} } -func (m *DatedPrice) String() string { return proto.CompactTextString(m) } -func (*DatedPrice) ProtoMessage() {} -func (*DatedPrice) Descriptor() ([]byte, []int) { +func (m *ExchangeRateAtBlock) Reset() { *m = ExchangeRateAtBlock{} } +func (m *ExchangeRateAtBlock) String() string { return proto.CompactTextString(m) } +func (*ExchangeRateAtBlock) ProtoMessage() {} +func (*ExchangeRateAtBlock) Descriptor() ([]byte, []int) { return fileDescriptor_43d45df86ea09ed4, []int{4} } -func (m *DatedPrice) XXX_Unmarshal(b []byte) error { +func (m *ExchangeRateAtBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *DatedPrice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *ExchangeRateAtBlock) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_DatedPrice.Marshal(b, m, deterministic) + return xxx_messageInfo_ExchangeRateAtBlock.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -281,25 +290,32 @@ func (m *DatedPrice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return b[:n], nil } } -func (m *DatedPrice) XXX_Merge(src proto.Message) { - xxx_messageInfo_DatedPrice.Merge(m, src) +func (m *ExchangeRateAtBlock) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExchangeRateAtBlock.Merge(m, src) } -func (m *DatedPrice) XXX_Size() int { +func (m *ExchangeRateAtBlock) XXX_Size() int { return m.Size() } -func (m *DatedPrice) XXX_DiscardUnknown() { - xxx_messageInfo_DatedPrice.DiscardUnknown(m) +func (m *ExchangeRateAtBlock) XXX_DiscardUnknown() { + xxx_messageInfo_ExchangeRateAtBlock.DiscardUnknown(m) } -var xxx_messageInfo_DatedPrice proto.InternalMessageInfo +var xxx_messageInfo_ExchangeRateAtBlock proto.InternalMessageInfo -func (m *DatedPrice) GetCreatedBlock() uint64 { +func (m *ExchangeRateAtBlock) GetCreatedBlock() uint64 { if m != nil { return m.CreatedBlock } return 0 } +func (m *ExchangeRateAtBlock) GetBlockTimestampMs() int64 { + if m != nil { + return m.BlockTimestampMs + } + return 0 +} + // Rewards defines a credit object towards validators // which provide prices faithfully for different pairs. type Rewards struct { @@ -371,76 +387,78 @@ func init() { proto.RegisterType((*AggregateExchangeRatePrevote)(nil), "nibiru.oracle.v1.AggregateExchangeRatePrevote") proto.RegisterType((*AggregateExchangeRateVote)(nil), "nibiru.oracle.v1.AggregateExchangeRateVote") proto.RegisterType((*ExchangeRateTuple)(nil), "nibiru.oracle.v1.ExchangeRateTuple") - proto.RegisterType((*DatedPrice)(nil), "nibiru.oracle.v1.DatedPrice") + proto.RegisterType((*ExchangeRateAtBlock)(nil), "nibiru.oracle.v1.ExchangeRateAtBlock") proto.RegisterType((*Rewards)(nil), "nibiru.oracle.v1.Rewards") } func init() { proto.RegisterFile("nibiru/oracle/v1/oracle.proto", fileDescriptor_43d45df86ea09ed4) } var fileDescriptor_43d45df86ea09ed4 = []byte{ - // 979 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xf6, 0xe6, 0x57, 0xe3, 0xb1, 0x53, 0x92, 0x69, 0x0a, 0x9b, 0x50, 0xbc, 0x61, 0x2a, 0x55, - 0x39, 0x94, 0x5d, 0xa5, 0x80, 0x10, 0x91, 0x38, 0xb0, 0x0d, 0x81, 0x48, 0x05, 0x59, 0xa3, 0x0a, - 0x24, 0x84, 0x64, 0xcd, 0xee, 0x4e, 0xd6, 0x23, 0xef, 0xee, 0x58, 0x33, 0xeb, 0x38, 0x91, 0x10, - 0x67, 0x8e, 0x3d, 0xa1, 0x1e, 0x73, 0xe6, 0x8e, 0xc4, 0x9f, 0xd0, 0x1b, 0x3d, 0xa2, 0x1e, 0xb6, - 0x28, 0xe1, 0x80, 0x10, 0x27, 0xff, 0x05, 0x68, 0x66, 0xc7, 0xf1, 0x26, 0xb6, 0x04, 0x01, 0xf5, - 0x64, 0xbf, 0xf7, 0xcd, 0x7c, 0xef, 0x7b, 0x3f, 0x66, 0x66, 0xc1, 0x5b, 0x19, 0x0b, 0x98, 0x18, - 0x78, 0x5c, 0x90, 0x30, 0xa1, 0xde, 0xd1, 0x8e, 0xf9, 0xe7, 0xf6, 0x05, 0xcf, 0x39, 0x5c, 0x2d, - 0x61, 0xd7, 0x38, 0x8f, 0x76, 0x36, 0xd7, 0x63, 0x1e, 0x73, 0x0d, 0x7a, 0xea, 0x5f, 0xb9, 0x6e, - 0xb3, 0x15, 0x73, 0x1e, 0x27, 0xd4, 0xd3, 0x56, 0x30, 0x38, 0xf4, 0xa2, 0x81, 0x20, 0x39, 0xe3, - 0xd9, 0x18, 0x0f, 0xb9, 0x4c, 0xb9, 0xf4, 0x02, 0x22, 0x55, 0x90, 0x80, 0xe6, 0x64, 0xc7, 0x0b, - 0x39, 0x33, 0x38, 0xfa, 0x65, 0x19, 0x2c, 0xb5, 0x89, 0x20, 0xa9, 0x84, 0x1f, 0x80, 0xc6, 0x11, - 0xcf, 0x69, 0xa7, 0x4f, 0x05, 0xe3, 0x91, 0x6d, 0x6d, 0x59, 0xdb, 0x0b, 0xfe, 0xeb, 0xa3, 0xc2, - 0x81, 0x27, 0x24, 0x4d, 0x76, 0x51, 0x05, 0x44, 0x18, 0x28, 0xab, 0xad, 0x0d, 0x98, 0x81, 0x9b, - 0x1a, 0xcb, 0xbb, 0x82, 0xca, 0x2e, 0x4f, 0x22, 0x7b, 0x6e, 0xcb, 0xda, 0xae, 0xfb, 0x9f, 0x3e, - 0x2b, 0x9c, 0xda, 0x8b, 0xc2, 0xb9, 0x17, 0xb3, 0xbc, 0x3b, 0x08, 0xdc, 0x90, 0xa7, 0x9e, 0x91, - 0x53, 0xfe, 0xbc, 0x23, 0xa3, 0x9e, 0x97, 0x9f, 0xf4, 0xa9, 0x74, 0xf7, 0x68, 0x38, 0x2a, 0x9c, - 0xdb, 0x95, 0x48, 0x17, 0x6c, 0x08, 0xaf, 0x28, 0xc7, 0xe3, 0xb1, 0x0d, 0x29, 0x68, 0x08, 0x3a, - 0x24, 0x22, 0xea, 0x04, 0x24, 0x8b, 0xec, 0x79, 0x1d, 0x6c, 0xef, 0xda, 0xc1, 0x4c, 0x5a, 0x15, - 0x2a, 0x84, 0x41, 0x69, 0xf9, 0x24, 0x8b, 0x60, 0x0c, 0xea, 0xc3, 0x2e, 0xcb, 0x69, 0xc2, 0x64, - 0x6e, 0x2f, 0x6c, 0xcd, 0x6f, 0xd7, 0xfd, 0x83, 0x17, 0x85, 0xb3, 0x53, 0x09, 0xf0, 0x85, 0x6e, - 0xd2, 0xc3, 0x2e, 0x61, 0x99, 0x67, 0xfa, 0x79, 0xec, 0x85, 0x3c, 0x4d, 0x79, 0xe6, 0x11, 0x29, - 0x69, 0xee, 0xb6, 0x09, 0x13, 0xa3, 0xc2, 0x59, 0x2d, 0x63, 0x5d, 0xf0, 0x21, 0x3c, 0xe1, 0x56, - 0xf5, 0x93, 0x09, 0x91, 0xdd, 0xce, 0xa1, 0x20, 0xa1, 0xea, 0x9d, 0xbd, 0xf8, 0xff, 0xea, 0x77, - 0x99, 0x0d, 0xe1, 0x15, 0xed, 0xd8, 0x37, 0x36, 0xdc, 0x05, 0xcd, 0x72, 0xc5, 0x90, 0x65, 0x11, - 0x1f, 0xda, 0x4b, 0xba, 0xd3, 0x6f, 0x8c, 0x0a, 0xe7, 0x56, 0x75, 0x7f, 0x89, 0x22, 0xdc, 0xd0, - 0xe6, 0x57, 0xda, 0x82, 0xdf, 0x81, 0xf5, 0x94, 0x65, 0x9d, 0x23, 0x92, 0xb0, 0x48, 0x0d, 0xc3, - 0x98, 0xe3, 0x86, 0x56, 0xfc, 0xf9, 0xb5, 0x15, 0xbf, 0x59, 0x46, 0x9c, 0xc5, 0x89, 0xf0, 0x5a, - 0xca, 0xb2, 0x2f, 0x95, 0xb7, 0x4d, 0x85, 0x89, 0xff, 0x83, 0x05, 0xd6, 0xf3, 0x21, 0xe9, 0x77, - 0x12, 0xce, 0x7b, 0x01, 0x09, 0x7b, 0x63, 0x01, 0xcb, 0x5b, 0xd6, 0x76, 0xe3, 0xc1, 0x86, 0x5b, - 0x9e, 0x07, 0x77, 0x7c, 0x1e, 0xdc, 0x3d, 0x73, 0x1e, 0xfc, 0x03, 0xa5, 0xed, 0xcf, 0xc2, 0x69, - 0xcd, 0xda, 0x7e, 0x9f, 0xa7, 0x2c, 0xa7, 0x69, 0x3f, 0x3f, 0x99, 0x68, 0x9a, 0xb5, 0x0e, 0x3d, - 0x7d, 0xe9, 0x58, 0x18, 0x2a, 0xe8, 0x91, 0x41, 0x8c, 0xb0, 0xf7, 0x00, 0xd0, 0x49, 0xf0, 0x9c, - 0x0a, 0x69, 0xd7, 0x75, 0x49, 0x6f, 0x8f, 0x0a, 0x67, 0xad, 0x92, 0xa0, 0xc6, 0x10, 0xae, 0xab, - 0xb4, 0xf4, 0x7f, 0xf8, 0x2d, 0xb8, 0xa5, 0xd3, 0x26, 0x39, 0x17, 0x9d, 0x43, 0x4a, 0x3b, 0x5a, - 0xac, 0x0d, 0x74, 0x35, 0x1f, 0x5d, 0xbb, 0x9a, 0x9b, 0xe6, 0xfc, 0x4c, 0x53, 0x22, 0xbc, 0x76, - 0xe1, 0xdd, 0xa7, 0x14, 0x2b, 0x1f, 0x3c, 0x00, 0x6b, 0xf4, 0xb8, 0xcf, 0xca, 0x02, 0x75, 0x82, - 0x84, 0x87, 0x3d, 0x69, 0x37, 0xb4, 0xf4, 0x3b, 0xa3, 0xc2, 0xb1, 0x4b, 0xb6, 0xa9, 0x25, 0x08, - 0xaf, 0x4e, 0x7c, 0xbe, 0x76, 0xed, 0x2e, 0x3f, 0x3d, 0x75, 0x6a, 0x7f, 0x9c, 0x3a, 0x16, 0xfa, - 0xc9, 0x02, 0x77, 0x3e, 0x8e, 0x63, 0x41, 0x63, 0x92, 0xd3, 0x4f, 0x8e, 0xc3, 0x2e, 0xc9, 0x62, - 0x15, 0x8f, 0xb6, 0x05, 0x55, 0x15, 0x80, 0x77, 0xc1, 0x42, 0x97, 0xc8, 0xae, 0xbe, 0x60, 0xea, - 0xfe, 0x6b, 0xa3, 0xc2, 0x69, 0x94, 0x81, 0x94, 0x17, 0x61, 0x0d, 0xc2, 0x7b, 0x60, 0x51, 0x97, - 0xcb, 0x5c, 0x25, 0xab, 0xa3, 0xc2, 0x69, 0x4e, 0x2e, 0x07, 0x81, 0x70, 0x09, 0xeb, 0x59, 0x1e, - 0x04, 0x29, 0xcb, 0x4b, 0x6d, 0xfa, 0x32, 0xb8, 0x3c, 0xcb, 0x15, 0x54, 0xcd, 0xb2, 0x36, 0xb5, - 0xe8, 0xdd, 0xe6, 0xf7, 0xa7, 0x4e, 0xcd, 0xe8, 0xae, 0xa1, 0xdf, 0x2d, 0xb0, 0x31, 0x53, 0xb7, - 0x6a, 0x15, 0x7c, 0x62, 0x81, 0x75, 0x6a, 0x9c, 0xaa, 0xa2, 0xb4, 0x93, 0x0f, 0xfa, 0x09, 0x95, - 0xb6, 0xb5, 0x35, 0xbf, 0xdd, 0x78, 0x70, 0xd7, 0xbd, 0x7a, 0x5f, 0xbb, 0x55, 0x8a, 0xc7, 0x6a, - 0xad, 0xff, 0xa1, 0xea, 0xe7, 0x64, 0xbe, 0x66, 0xd1, 0xa1, 0x1f, 0x5f, 0x3a, 0x70, 0x6a, 0xa7, - 0xc4, 0x90, 0x4e, 0xf9, 0xfe, 0x6d, 0x89, 0xae, 0xa4, 0xf9, 0x97, 0x05, 0xd6, 0xa6, 0x02, 0xc0, - 0x6f, 0xc0, 0x42, 0x9f, 0x30, 0x61, 0x7a, 0xf2, 0x99, 0x19, 0xbc, 0xff, 0x74, 0xd5, 0x99, 0x66, - 0x2a, 0x3a, 0x84, 0x35, 0x2b, 0xec, 0x81, 0x95, 0x4b, 0xc9, 0x1a, 0xc5, 0xfb, 0xd7, 0x9e, 0xef, - 0xf5, 0x19, 0x95, 0x43, 0xb8, 0x59, 0x2d, 0xce, 0x95, 0x74, 0x7f, 0xb6, 0x00, 0xd8, 0x23, 0x39, - 0x8d, 0xda, 0x82, 0x85, 0x74, 0x5a, 0x89, 0xf5, 0xea, 0x94, 0xc0, 0x8f, 0xc0, 0x4a, 0x28, 0xa8, - 0x0a, 0x6e, 0x86, 0x73, 0x4e, 0x0f, 0xa7, 0x3d, 0xd9, 0x7e, 0x09, 0x46, 0xb8, 0x69, 0x6c, 0x3d, - 0x9e, 0x48, 0x82, 0x1b, 0x58, 0xbf, 0x46, 0x12, 0xde, 0x04, 0x73, 0xcc, 0xbc, 0xc8, 0x78, 0x8e, - 0x45, 0xf0, 0x6d, 0xd0, 0xac, 0xbc, 0xc6, 0xb2, 0x24, 0xc6, 0x8d, 0xc9, 0x9b, 0x2c, 0xe1, 0xfb, - 0x60, 0x51, 0x3d, 0xf3, 0xd2, 0x9e, 0xd7, 0x03, 0xba, 0xe1, 0x96, 0x89, 0xb8, 0xea, 0x43, 0xc0, - 0x35, 0x1f, 0x02, 0xee, 0x43, 0xce, 0x32, 0x7f, 0x41, 0x25, 0x8f, 0xcb, 0xd5, 0xfe, 0xfe, 0xb3, - 0xb3, 0x96, 0xf5, 0xfc, 0xac, 0x65, 0xfd, 0x76, 0xd6, 0xb2, 0x9e, 0x9c, 0xb7, 0x6a, 0xcf, 0xcf, - 0x5b, 0xb5, 0x5f, 0xcf, 0x5b, 0xb5, 0xaf, 0xef, 0xff, 0xd3, 0x30, 0x98, 0x2f, 0x19, 0x5d, 0xa5, - 0x60, 0x49, 0x5f, 0xc0, 0xef, 0xfe, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x73, 0x48, 0x19, 0x08, 0xe7, - 0x08, 0x00, 0x00, + // 1009 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xf7, 0x26, 0x4e, 0x1a, 0x8f, 0x93, 0x92, 0x4c, 0x52, 0xd8, 0x84, 0xd6, 0x1b, 0xa6, 0x52, + 0x95, 0x03, 0xec, 0x2a, 0xa1, 0x08, 0x11, 0x89, 0x43, 0xb7, 0x21, 0x28, 0x6a, 0x8a, 0xa2, 0x51, + 0x04, 0x12, 0x97, 0xd5, 0x78, 0x77, 0x62, 0x0f, 0xf1, 0xee, 0x58, 0x3b, 0xe3, 0x38, 0x91, 0x10, + 0x67, 0x4e, 0xa8, 0x27, 0xc4, 0x31, 0x67, 0x6e, 0x7c, 0x8b, 0x9e, 0x50, 0x8f, 0xa8, 0x87, 0x6d, + 0x95, 0x5c, 0x2a, 0xb8, 0xf9, 0x13, 0xa0, 0xf9, 0xe3, 0x78, 0x83, 0x7d, 0x68, 0x40, 0x9c, 0x76, + 0xdf, 0x7b, 0x33, 0xbf, 0xf7, 0x7b, 0x7f, 0xe6, 0xcd, 0x80, 0x7b, 0x19, 0x6b, 0xb2, 0xbc, 0x17, + 0xf0, 0x9c, 0xc4, 0x1d, 0x1a, 0x9c, 0x6c, 0xda, 0x3f, 0xbf, 0x9b, 0x73, 0xc9, 0xe1, 0xa2, 0x31, + 0xfb, 0x56, 0x79, 0xb2, 0xb9, 0xb6, 0xd2, 0xe2, 0x2d, 0xae, 0x8d, 0x81, 0xfa, 0x33, 0xeb, 0xd6, + 0x1a, 0x2d, 0xce, 0x5b, 0x1d, 0x1a, 0x68, 0xa9, 0xd9, 0x3b, 0x0a, 0x92, 0x5e, 0x4e, 0x24, 0xe3, + 0xd9, 0xd0, 0x1e, 0x73, 0x91, 0x72, 0x11, 0x34, 0x89, 0x50, 0x4e, 0x9a, 0x54, 0x92, 0xcd, 0x20, + 0xe6, 0xcc, 0xda, 0xd1, 0xef, 0x73, 0x60, 0xf6, 0x80, 0xe4, 0x24, 0x15, 0xf0, 0x53, 0x50, 0x3f, + 0xe1, 0x92, 0x46, 0x5d, 0x9a, 0x33, 0x9e, 0xb8, 0xce, 0xba, 0xb3, 0x51, 0x0d, 0xdf, 0x1d, 0x14, + 0x1e, 0x3c, 0x23, 0x69, 0x67, 0x1b, 0x95, 0x8c, 0x08, 0x03, 0x25, 0x1d, 0x68, 0x01, 0x66, 0xe0, + 0xb6, 0xb6, 0xc9, 0x76, 0x4e, 0x45, 0x9b, 0x77, 0x12, 0x77, 0x6a, 0xdd, 0xd9, 0xa8, 0x85, 0x5f, + 0x3e, 0x2f, 0xbc, 0xca, 0xcb, 0xc2, 0x7b, 0xd0, 0x62, 0xb2, 0xdd, 0x6b, 0xfa, 0x31, 0x4f, 0x03, + 0x4b, 0xc7, 0x7c, 0x3e, 0x12, 0xc9, 0x71, 0x20, 0xcf, 0xba, 0x54, 0xf8, 0x3b, 0x34, 0x1e, 0x14, + 0xde, 0x9d, 0x92, 0xa7, 0x2b, 0x34, 0x84, 0x17, 0x94, 0xe2, 0x70, 0x28, 0x43, 0x0a, 0xea, 0x39, + 0xed, 0x93, 0x3c, 0x89, 0x9a, 0x24, 0x4b, 0xdc, 0x69, 0xed, 0x6c, 0xe7, 0xc6, 0xce, 0x6c, 0x58, + 0x25, 0x28, 0x84, 0x81, 0x91, 0x42, 0x92, 0x25, 0xf0, 0x3b, 0x50, 0xeb, 0xb7, 0x99, 0xa4, 0x1d, + 0x26, 0xa4, 0x5b, 0x5d, 0x9f, 0xde, 0xa8, 0x85, 0xfb, 0x2f, 0x0b, 0xef, 0x61, 0xc9, 0xc1, 0x57, + 0xba, 0x48, 0x8f, 0xdb, 0x84, 0x65, 0x81, 0xad, 0xe7, 0xc9, 0x56, 0x70, 0x1a, 0xc4, 0x3c, 0x4d, + 0x79, 0x16, 0x10, 0x21, 0xa8, 0xf4, 0x0f, 0x08, 0xcb, 0x07, 0x85, 0xb7, 0x68, 0xdc, 0x5d, 0x41, + 0x22, 0x3c, 0x82, 0x57, 0x29, 0x14, 0x1d, 0x22, 0xda, 0xd1, 0x51, 0x4e, 0x62, 0x55, 0x3e, 0x77, + 0xe6, 0xbf, 0xa5, 0xf0, 0x3a, 0x1a, 0xc2, 0x0b, 0x5a, 0xb1, 0x6b, 0x65, 0xb8, 0x0d, 0xe6, 0xcd, + 0x8a, 0x3e, 0xcb, 0x12, 0xde, 0x77, 0x67, 0x75, 0xb1, 0xdf, 0x1b, 0x14, 0xde, 0x72, 0x79, 0xbf, + 0xb1, 0x22, 0x5c, 0xd7, 0xe2, 0x37, 0x5a, 0x82, 0x3f, 0x80, 0x95, 0x94, 0x65, 0xd1, 0x09, 0xe9, + 0xb0, 0x44, 0xf5, 0xc3, 0x10, 0xe3, 0x96, 0x66, 0xfc, 0xf4, 0xc6, 0x8c, 0xdf, 0x37, 0x1e, 0x27, + 0x61, 0x22, 0xbc, 0x94, 0xb2, 0xec, 0x6b, 0xa5, 0x3d, 0xa0, 0xb9, 0xf5, 0xff, 0xb3, 0x03, 0x56, + 0x64, 0x9f, 0x74, 0xa3, 0x0e, 0xe7, 0xc7, 0x4d, 0x12, 0x1f, 0x0f, 0x09, 0xcc, 0xad, 0x3b, 0x1b, + 0xf5, 0xad, 0x55, 0xdf, 0x1c, 0x09, 0x7f, 0x78, 0x24, 0xfc, 0x1d, 0x7b, 0x24, 0xc2, 0x3d, 0xc5, + 0xed, 0xcf, 0xc2, 0x6b, 0x4c, 0xda, 0xfe, 0x21, 0x4f, 0x99, 0xa4, 0x69, 0x57, 0x9e, 0x8d, 0x38, + 0x4d, 0x5a, 0x87, 0x7e, 0x79, 0xe5, 0x39, 0x18, 0x2a, 0xd3, 0xbe, 0xb5, 0x58, 0x62, 0x0f, 0x01, + 0xd0, 0x41, 0x70, 0x49, 0x73, 0xe1, 0xd6, 0x74, 0x4a, 0xef, 0x0c, 0x0a, 0x6f, 0xa9, 0x14, 0xa0, + 0xb6, 0x21, 0x5c, 0x53, 0x61, 0xe9, 0x7f, 0xf8, 0x3d, 0x58, 0xd6, 0x61, 0x13, 0xc9, 0xf3, 0xe8, + 0x88, 0xd2, 0x48, 0x93, 0x75, 0x81, 0xce, 0xe6, 0xfe, 0x8d, 0xb3, 0xb9, 0x66, 0x8f, 0xd0, 0x38, + 0x24, 0xc2, 0x4b, 0x57, 0xda, 0x5d, 0x4a, 0xb1, 0xd2, 0xc1, 0x3d, 0xb0, 0x44, 0x4f, 0xbb, 0xcc, + 0x24, 0x28, 0x6a, 0x76, 0x78, 0x7c, 0x2c, 0xdc, 0xba, 0xa6, 0x7e, 0x77, 0x50, 0x78, 0xae, 0x41, + 0x1b, 0x5b, 0x82, 0xf0, 0xe2, 0x48, 0x17, 0x6a, 0xd5, 0x76, 0xf5, 0xcd, 0xb9, 0xe7, 0xa0, 0xdf, + 0x1c, 0x70, 0xf7, 0x51, 0xab, 0x95, 0xd3, 0x16, 0x91, 0xf4, 0x8b, 0xd3, 0xb8, 0x4d, 0xb2, 0x96, + 0xf2, 0x45, 0x0f, 0x72, 0xaa, 0xa2, 0x87, 0xf7, 0x41, 0xb5, 0x4d, 0x44, 0x5b, 0xcf, 0x97, 0x5a, + 0xf8, 0xce, 0xa0, 0xf0, 0xea, 0xc6, 0x89, 0xd2, 0x22, 0xac, 0x8d, 0xf0, 0x01, 0x98, 0xd1, 0xa9, + 0xb2, 0x93, 0x64, 0x71, 0x50, 0x78, 0xf3, 0xa3, 0xd9, 0x90, 0x23, 0x6c, 0xcc, 0xba, 0x8f, 0x7b, + 0xcd, 0x94, 0x49, 0xc3, 0x4b, 0xcf, 0x82, 0xeb, 0x7d, 0x5c, 0xb2, 0xaa, 0x3e, 0xd6, 0xa2, 0x26, + 0xbc, 0x3d, 0xf7, 0xe3, 0xb9, 0x57, 0x79, 0x73, 0xee, 0x55, 0xd0, 0x6b, 0x07, 0xac, 0x4e, 0xe4, + 0xac, 0x4a, 0x04, 0x9f, 0x39, 0x60, 0x85, 0x5a, 0xa5, 0xca, 0x24, 0x8d, 0x64, 0xaf, 0xdb, 0xa1, + 0xc2, 0x75, 0xd6, 0xa7, 0x37, 0xea, 0x5b, 0xf7, 0xfd, 0x7f, 0x8e, 0x6a, 0xbf, 0x0c, 0x71, 0xa8, + 0xd6, 0x86, 0x9f, 0xa9, 0x3a, 0x8e, 0xfa, 0x6a, 0x12, 0x1c, 0xfa, 0xf5, 0x95, 0x07, 0xc7, 0x76, + 0x0a, 0x0c, 0xe9, 0x98, 0xee, 0x6d, 0xd3, 0x53, 0x0a, 0xf1, 0x2f, 0x07, 0x2c, 0x8d, 0x81, 0xc3, + 0x08, 0x54, 0xbb, 0x84, 0xe5, 0xb6, 0x16, 0x4f, 0x6c, 0xb3, 0xfd, 0xdb, 0x09, 0x67, 0xeb, 0xa8, + 0x10, 0x11, 0xd6, 0xc0, 0xf0, 0x18, 0x2c, 0x5c, 0x8b, 0xd5, 0x12, 0xde, 0xbd, 0x71, 0x5b, 0xaf, + 0x4c, 0x48, 0x1c, 0xc2, 0xf3, 0xe5, 0xdc, 0x94, 0xa2, 0xfd, 0x69, 0x0a, 0x2c, 0x97, 0xa3, 0x7d, + 0x64, 0x4a, 0x3e, 0x4e, 0xc7, 0xf9, 0xff, 0xe8, 0xc0, 0xcf, 0xc1, 0x42, 0x9c, 0x53, 0x22, 0x69, + 0x62, 0x9b, 0x73, 0x4a, 0x37, 0xa7, 0x3b, 0xda, 0x7e, 0xcd, 0x8c, 0xf0, 0xbc, 0x95, 0x0d, 0xd7, + 0x27, 0x00, 0x6a, 0x7d, 0x24, 0x59, 0x4a, 0x85, 0x24, 0x69, 0x37, 0x4a, 0x85, 0x6e, 0xf0, 0xe9, + 0xf0, 0xde, 0xa0, 0xf0, 0x56, 0x0d, 0xc6, 0xf8, 0x1a, 0x84, 0x17, 0xb5, 0xf2, 0x70, 0xa8, 0x7b, + 0x2a, 0x90, 0x00, 0xb7, 0xb0, 0xbe, 0xd9, 0x04, 0xbc, 0x0d, 0xa6, 0x98, 0xbd, 0xdd, 0xf1, 0x14, + 0x4b, 0xe0, 0x07, 0x60, 0xbe, 0x74, 0xb3, 0x0b, 0xc3, 0x12, 0xd7, 0x47, 0xf7, 0xbb, 0x80, 0x9f, + 0x80, 0x19, 0xf5, 0x64, 0x50, 0xde, 0xa7, 0xf5, 0x84, 0x35, 0x59, 0xf1, 0xd5, 0xa3, 0xc2, 0xb7, + 0x8f, 0x0a, 0xff, 0x31, 0x67, 0x59, 0x58, 0x55, 0x99, 0xc4, 0x66, 0x75, 0xb8, 0xf7, 0xfc, 0xa2, + 0xe1, 0xbc, 0xb8, 0x68, 0x38, 0xaf, 0x2f, 0x1a, 0xce, 0xb3, 0xcb, 0x46, 0xe5, 0xc5, 0x65, 0xa3, + 0xf2, 0xc7, 0x65, 0xa3, 0xf2, 0x6d, 0xf0, 0x16, 0x1d, 0x66, 0x1f, 0x46, 0x3a, 0xeb, 0xcd, 0x59, + 0x3d, 0xcc, 0x3f, 0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xa2, 0x1f, 0x50, 0xdb, 0x36, 0x09, 0x00, + 0x00, } func (this *Params) Equal(that interface{}) bool { @@ -746,7 +764,7 @@ func (m *ExchangeRateTuple) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *DatedPrice) Marshal() (dAtA []byte, err error) { +func (m *ExchangeRateAtBlock) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -756,16 +774,21 @@ func (m *DatedPrice) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *DatedPrice) MarshalTo(dAtA []byte) (int, error) { +func (m *ExchangeRateAtBlock) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *DatedPrice) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ExchangeRateAtBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l + if m.BlockTimestampMs != 0 { + i = encodeVarintOracle(dAtA, i, uint64(m.BlockTimestampMs)) + i-- + dAtA[i] = 0x18 + } if m.CreatedBlock != 0 { i = encodeVarintOracle(dAtA, i, uint64(m.CreatedBlock)) i-- @@ -933,7 +956,7 @@ func (m *ExchangeRateTuple) Size() (n int) { return n } -func (m *DatedPrice) Size() (n int) { +func (m *ExchangeRateAtBlock) Size() (n int) { if m == nil { return 0 } @@ -944,6 +967,9 @@ func (m *DatedPrice) Size() (n int) { if m.CreatedBlock != 0 { n += 1 + sovOracle(uint64(m.CreatedBlock)) } + if m.BlockTimestampMs != 0 { + n += 1 + sovOracle(uint64(m.BlockTimestampMs)) + } return n } @@ -1120,7 +1146,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - var v github_com_NibiruChain_nibiru_x_common_asset.Pair + var v github_com_NibiruChain_nibiru_v2_x_common_asset.Pair m.Whitelist = append(m.Whitelist, v) if err := m.Whitelist[len(m.Whitelist)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1706,7 +1732,7 @@ func (m *ExchangeRateTuple) Unmarshal(dAtA []byte) error { } return nil } -func (m *DatedPrice) Unmarshal(dAtA []byte) error { +func (m *ExchangeRateAtBlock) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1729,10 +1755,10 @@ func (m *DatedPrice) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DatedPrice: wiretype end group for non-group") + return fmt.Errorf("proto: ExchangeRateAtBlock: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DatedPrice: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ExchangeRateAtBlock: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -1788,6 +1814,25 @@ func (m *DatedPrice) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockTimestampMs", wireType) + } + m.BlockTimestampMs = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOracle + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockTimestampMs |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipOracle(dAtA[iNdEx:]) diff --git a/x/oracle/types/params.go b/x/oracle/types/params.go index 65be8d23f..67334aeda 100644 --- a/x/oracle/types/params.go +++ b/x/oracle/types/params.go @@ -4,12 +4,10 @@ import ( "fmt" time "time" - "gopkg.in/yaml.v2" + "cosmossdk.io/math" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/denoms" - - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" ) // Parameter keys @@ -37,8 +35,8 @@ const ( // Default parameter values var ( - DefaultVoteThreshold = sdk.OneDec().Quo(sdk.NewDec(3)) // 33.33% - DefaultRewardBand = sdk.NewDecWithPrec(2, 2) // 2% (-1, 1) + DefaultVoteThreshold = math.LegacyOneDec().Quo(math.LegacyNewDec(3)) // 33.33% + DefaultRewardBand = math.LegacyNewDecWithPrec(2, 2) // 2% (-1, 1) DefaultWhitelist = []asset.Pair{ // paired against the US fiat dollar asset.Registry.Pair(denoms.BTC, denoms.USD), @@ -52,10 +50,10 @@ var ( // asset.Registry.Pair(denoms.SOL, denoms.USD), // asset.Registry.Pair(denoms.ADA, denoms.USD), } - DefaultSlashFraction = sdk.NewDecWithPrec(5, 3) // 0.5% - DefaultMinValidPerWindow = sdk.NewDecWithPrec(69, 2) // 69% - DefaultTwapLookbackWindow = time.Duration(15 * time.Minute) // 15 minutes - DefaultValidatorFeeRatio = sdk.NewDecWithPrec(5, 2) // 0.05% + DefaultSlashFraction = math.LegacyNewDecWithPrec(5, 3) // 0.5% + DefaultMinValidPerWindow = math.LegacyNewDecWithPrec(69, 2) // 69% + DefaultTwapLookbackWindow = time.Duration(15 * time.Minute) // 15 minutes + DefaultValidatorFeeRatio = math.LegacyNewDecWithPrec(5, 2) // 0.05% ) // DefaultParams creates default oracle module parameters @@ -75,19 +73,13 @@ func DefaultParams() Params { } } -// String implements fmt.Stringer interface -func (p Params) String() string { - out, _ := yaml.Marshal(p) - return string(out) -} - // Validate performs basic validation on oracle parameters. func (p Params) Validate() error { if p.VotePeriod == 0 { return fmt.Errorf("oracle parameter VotePeriod must be > 0, is %d", p.VotePeriod) } - if p.VoteThreshold.LTE(sdk.NewDecWithPrec(33, 2)) { + if p.VoteThreshold.LTE(math.LegacyNewDecWithPrec(33, 2)) { return fmt.Errorf("oracle parameter VoteThreshold must be greater than 33 percent") } @@ -95,11 +87,11 @@ func (p Params) Validate() error { return fmt.Errorf("oracle parameter MinVoters must be greater than 0") } - if p.RewardBand.GT(sdk.OneDec()) || p.RewardBand.IsNegative() { + if p.RewardBand.GT(math.LegacyOneDec()) || p.RewardBand.IsNegative() { return fmt.Errorf("oracle parameter RewardBand must be between [0, 1]") } - if p.SlashFraction.GT(sdk.OneDec()) || p.SlashFraction.IsNegative() { + if p.SlashFraction.GT(math.LegacyOneDec()) || p.SlashFraction.IsNegative() { return fmt.Errorf("oracle parameter SlashFraction must be between [0, 1]") } @@ -107,11 +99,11 @@ func (p Params) Validate() error { return fmt.Errorf("oracle parameter SlashWindow must be greater than or equal with VotePeriod") } - if p.MinValidPerWindow.GT(sdk.OneDec()) || p.MinValidPerWindow.IsNegative() { + if p.MinValidPerWindow.GT(math.LegacyOneDec()) || p.MinValidPerWindow.IsNegative() { return fmt.Errorf("oracle parameter MinValidPerWindow must be between [0, 1]") } - if p.ValidatorFeeRatio.GT(sdk.OneDec()) || p.ValidatorFeeRatio.IsNegative() { + if p.ValidatorFeeRatio.GT(math.LegacyOneDec()) || p.ValidatorFeeRatio.IsNegative() { return fmt.Errorf("oracle parameter ValidatorFeeRatio must be between [0, 1]") } diff --git a/x/oracle/types/params_test.go b/x/oracle/types/params_test.go index 5b3a8803e..0aba109ba 100644 --- a/x/oracle/types/params_test.go +++ b/x/oracle/types/params_test.go @@ -3,10 +3,10 @@ package types_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestParamsEqual(t *testing.T) { @@ -25,25 +25,25 @@ func TestParamsEqual(t *testing.T) { // small vote threshold p2 := types.DefaultParams() - p2.VoteThreshold = sdk.ZeroDec() + p2.VoteThreshold = math.LegacyZeroDec() err = p2.Validate() require.Error(t, err) // negative reward band p3 := types.DefaultParams() - p3.RewardBand = sdk.NewDecWithPrec(-1, 2) + p3.RewardBand = math.LegacyNewDecWithPrec(-1, 2) err = p3.Validate() require.Error(t, err) // negative slash fraction p4 := types.DefaultParams() - p4.SlashFraction = sdk.NewDec(-1) + p4.SlashFraction = math.LegacyNewDec(-1) err = p4.Validate() require.Error(t, err) // negative min valid per window p5 := types.DefaultParams() - p5.MinValidPerWindow = sdk.NewDec(-1) + p5.MinValidPerWindow = math.LegacyNewDec(-1) err = p5.Validate() require.Error(t, err) @@ -61,13 +61,13 @@ func TestParamsEqual(t *testing.T) { // oracle fee ratio > 1 p12 := types.DefaultParams() - p12.ValidatorFeeRatio = sdk.NewDec(2) + p12.ValidatorFeeRatio = math.LegacyNewDec(2) err = p12.Validate() require.Error(t, err) // oracle fee ratio < 0 p13 := types.DefaultParams() - p13.ValidatorFeeRatio = sdk.NewDec(-1) + p13.ValidatorFeeRatio = math.LegacyNewDec(-1) err = p13.Validate() require.Error(t, err) diff --git a/x/oracle/types/query.pb.go b/x/oracle/types/query.pb.go index 3d82a1d37..9498fb7d9 100644 --- a/x/oracle/types/query.pb.go +++ b/x/oracle/types/query.pb.go @@ -6,7 +6,7 @@ package types import ( context "context" fmt "fmt" - github_com_NibiruChain_nibiru_x_common_asset "github.com/NibiruChain/nibiru/x/common/asset" + github_com_NibiruChain_nibiru_v2_x_common_asset "github.com/NibiruChain/nibiru/v2/x/common/asset" _ "github.com/cosmos/cosmos-sdk/types" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" @@ -36,7 +36,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // method. type QueryExchangeRateRequest struct { // pair defines the pair to query for. - Pair github_com_NibiruChain_nibiru_x_common_asset.Pair `protobuf:"bytes,1,opt,name=pair,proto3,customtype=github.com/NibiruChain/nibiru/x/common/asset.Pair" json:"pair"` + Pair github_com_NibiruChain_nibiru_v2_x_common_asset.Pair `protobuf:"bytes,1,opt,name=pair,proto3,customtype=github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" json:"pair"` } func (m *QueryExchangeRateRequest) Reset() { *m = QueryExchangeRateRequest{} } @@ -77,6 +77,12 @@ var xxx_messageInfo_QueryExchangeRateRequest proto.InternalMessageInfo type QueryExchangeRateResponse struct { // exchange_rate defines the exchange rate of assets voted by validators ExchangeRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=exchange_rate,json=exchangeRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"exchange_rate"` + // Block timestamp for the block where the oracle came to consensus for this + // price. This timestamp is a conventional Unix millisecond time, i.e. the + // number of milliseconds elapsed since January 1, 1970 UTC. + BlockTimestampMs int64 `protobuf:"varint,2,opt,name=block_timestamp_ms,json=blockTimestampMs,proto3" json:"block_timestamp_ms,omitempty"` + // Block height when the oracle came to consensus for this price. + BlockHeight uint64 `protobuf:"varint,3,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` } func (m *QueryExchangeRateResponse) Reset() { *m = QueryExchangeRateResponse{} } @@ -112,6 +118,20 @@ func (m *QueryExchangeRateResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryExchangeRateResponse proto.InternalMessageInfo +func (m *QueryExchangeRateResponse) GetBlockTimestampMs() int64 { + if m != nil { + return m.BlockTimestampMs + } + return 0 +} + +func (m *QueryExchangeRateResponse) GetBlockHeight() uint64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + // QueryExchangeRatesRequest is the request type for the Query/ExchangeRates RPC // method. type QueryExchangeRatesRequest struct { @@ -239,7 +259,7 @@ var xxx_messageInfo_QueryActivesRequest proto.InternalMessageInfo // Query/Actives RPC method. type QueryActivesResponse struct { // actives defines a list of the pair which oracle prices agreed upon. - Actives []github_com_NibiruChain_nibiru_x_common_asset.Pair `protobuf:"bytes,1,rep,name=actives,proto3,customtype=github.com/NibiruChain/nibiru/x/common/asset.Pair" json:"actives"` + Actives []github_com_NibiruChain_nibiru_v2_x_common_asset.Pair `protobuf:"bytes,1,rep,name=actives,proto3,customtype=github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" json:"actives"` } func (m *QueryActivesResponse) Reset() { *m = QueryActivesResponse{} } @@ -318,7 +338,7 @@ var xxx_messageInfo_QueryVoteTargetsRequest proto.InternalMessageInfo type QueryVoteTargetsResponse struct { // vote_targets defines a list of the pairs in which everyone // should vote in the current vote period. - VoteTargets []github_com_NibiruChain_nibiru_x_common_asset.Pair `protobuf:"bytes,1,rep,name=vote_targets,json=voteTargets,proto3,customtype=github.com/NibiruChain/nibiru/x/common/asset.Pair" json:"vote_targets"` + VoteTargets []github_com_NibiruChain_nibiru_v2_x_common_asset.Pair `protobuf:"bytes,1,rep,name=vote_targets,json=voteTargets,proto3,customtype=github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" json:"vote_targets"` } func (m *QueryVoteTargetsResponse) Reset() { *m = QueryVoteTargetsResponse{} } @@ -987,77 +1007,80 @@ func init() { func init() { proto.RegisterFile("nibiru/oracle/v1/query.proto", fileDescriptor_16aef2382d1249a8) } var fileDescriptor_16aef2382d1249a8 = []byte{ - // 1105 bytes of a gzipped FileDescriptorProto + // 1166 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x97, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xc7, 0x3d, 0x50, 0x52, 0x18, 0xc7, 0xae, 0x33, 0x04, 0x91, 0x6e, 0x13, 0xbb, 0x2c, 0x4d, - 0xd4, 0x36, 0xc9, 0x2e, 0x4e, 0x50, 0x51, 0xf8, 0x21, 0x70, 0x12, 0x22, 0x81, 0x1a, 0x08, 0xa6, - 0x8a, 0x50, 0x85, 0x64, 0x8d, 0xd7, 0xd3, 0xcd, 0xaa, 0xf6, 0xce, 0x76, 0x67, 0x6d, 0x12, 0x01, - 0x97, 0x0a, 0x10, 0x47, 0x24, 0x84, 0xb8, 0x41, 0x2f, 0x48, 0x88, 0x33, 0x70, 0x87, 0x53, 0x8f, - 0x95, 0xb8, 0x20, 0x0e, 0x05, 0x25, 0x1c, 0xf8, 0x33, 0xd0, 0xce, 0x8c, 0xd7, 0xbb, 0x5e, 0x8f, - 0xbc, 0x38, 0xe2, 0x94, 0x68, 0xde, 0xdb, 0xf7, 0xfd, 0xbc, 0x37, 0xb3, 0xf3, 0xf5, 0xc2, 0x79, - 0xd7, 0x69, 0x3a, 0x7e, 0xd7, 0xa4, 0x3e, 0xb6, 0xda, 0xc4, 0xec, 0x55, 0xcd, 0x3b, 0x5d, 0xe2, - 0x1f, 0x19, 0x9e, 0x4f, 0x03, 0x8a, 0x4a, 0x22, 0x6a, 0x88, 0xa8, 0xd1, 0xab, 0x6a, 0xb3, 0x36, - 0xb5, 0x29, 0x0f, 0x9a, 0xe1, 0x7f, 0x22, 0x4f, 0x9b, 0xb7, 0x29, 0xb5, 0xdb, 0xc4, 0xc4, 0x9e, - 0x63, 0x62, 0xd7, 0xa5, 0x01, 0x0e, 0x1c, 0xea, 0x32, 0x19, 0x5d, 0x48, 0x69, 0xc8, 0x7a, 0x22, - 0x5c, 0xb6, 0x28, 0xeb, 0x50, 0x66, 0x36, 0x31, 0x0b, 0x83, 0x4d, 0x12, 0xe0, 0xaa, 0x69, 0x51, - 0xc7, 0x15, 0x71, 0x9d, 0xc1, 0xb9, 0x77, 0x42, 0xa6, 0xd7, 0x0f, 0xad, 0x03, 0xec, 0xda, 0xa4, - 0x8e, 0x03, 0x52, 0x27, 0x77, 0xba, 0x84, 0x05, 0x68, 0x17, 0x9e, 0xf1, 0xb0, 0xe3, 0xcf, 0x81, - 0x8b, 0xe0, 0xf2, 0x13, 0x9b, 0x1b, 0xf7, 0x1f, 0x56, 0x72, 0x7f, 0x3c, 0xac, 0x54, 0x6d, 0x27, - 0x38, 0xe8, 0x36, 0x0d, 0x8b, 0x76, 0xcc, 0xb7, 0xb8, 0xf6, 0xd6, 0x01, 0x76, 0x5c, 0x53, 0x72, - 0x1c, 0x9a, 0x16, 0xed, 0x74, 0xa8, 0x6b, 0x62, 0xc6, 0x48, 0x60, 0xec, 0x61, 0xc7, 0xaf, 0xf3, - 0x32, 0x2f, 0x3e, 0xfe, 0xf9, 0xbd, 0x4a, 0xee, 0x9f, 0x7b, 0x95, 0x9c, 0xee, 0xc1, 0xf3, 0x23, - 0x44, 0x99, 0x47, 0x5d, 0x46, 0xd0, 0xbb, 0xb0, 0x40, 0xe4, 0x7a, 0xc3, 0xc7, 0x01, 0x91, 0xf2, - 0x86, 0x94, 0x5f, 0x8a, 0xc9, 0xcb, 0xde, 0xc4, 0x9f, 0x55, 0xd6, 0xba, 0x6d, 0x06, 0x47, 0x1e, - 0x61, 0xc6, 0x36, 0xb1, 0xea, 0xd3, 0x24, 0x56, 0x5c, 0xbf, 0x30, 0x42, 0x91, 0xc9, 0x3e, 0xf5, - 0x4f, 0x00, 0xd4, 0x46, 0x45, 0x25, 0xd0, 0x2d, 0x58, 0x4c, 0x00, 0xb1, 0x39, 0x70, 0xf1, 0xd1, - 0xcb, 0xf9, 0xb5, 0x67, 0x8d, 0xe1, 0x0d, 0x34, 0xe2, 0x05, 0x6e, 0x74, 0xbd, 0x36, 0xd9, 0xd4, - 0x42, 0xec, 0x1f, 0xfe, 0xac, 0xa0, 0x54, 0x88, 0xd5, 0x0b, 0x71, 0x44, 0xa6, 0x3f, 0x05, 0x9f, - 0xe4, 0x14, 0x35, 0x2b, 0x70, 0x7a, 0x03, 0xba, 0xdb, 0x70, 0x36, 0xb9, 0x1c, 0xcd, 0xe9, 0x2c, - 0x16, 0x4b, 0x9c, 0xe7, 0x54, 0x1b, 0xd4, 0xaf, 0xa4, 0x9f, 0x87, 0x4f, 0x73, 0xb1, 0x7d, 0x1a, - 0x90, 0x1b, 0xd8, 0xb7, 0x49, 0x10, 0x71, 0x1c, 0xca, 0x93, 0x92, 0x08, 0x49, 0x96, 0xf7, 0xe1, - 0x74, 0x8f, 0x06, 0xa4, 0x11, 0x88, 0xf5, 0xd3, 0x03, 0xe5, 0x7b, 0x03, 0x15, 0xfd, 0x6d, 0x38, - 0xcf, 0x95, 0x77, 0x08, 0x69, 0x11, 0x7f, 0x9b, 0xb4, 0x89, 0xcd, 0x5f, 0x81, 0xfe, 0x39, 0x5d, - 0x84, 0xc5, 0x1e, 0x6e, 0x3b, 0x2d, 0x1c, 0x50, 0xbf, 0x81, 0x5b, 0x2d, 0x79, 0x62, 0xeb, 0x85, - 0x68, 0xb5, 0xd6, 0x6a, 0xc5, 0xcf, 0xdf, 0x6b, 0x70, 0x41, 0x51, 0x50, 0xf6, 0x53, 0x81, 0xf9, - 0x5b, 0x3c, 0x16, 0x2f, 0x07, 0xc5, 0x52, 0x58, 0x4b, 0x7f, 0x53, 0xce, 0x69, 0xd7, 0x61, 0x6c, - 0x8b, 0x76, 0xdd, 0x80, 0xf8, 0x13, 0xd3, 0xbc, 0x22, 0x07, 0x9b, 0xa8, 0x25, 0x41, 0x9e, 0x81, - 0xd3, 0x1d, 0x87, 0xb1, 0x86, 0x25, 0xd6, 0x79, 0xa9, 0x33, 0xf5, 0x7c, 0x67, 0x90, 0x1a, 0x4d, - 0xa7, 0x66, 0xdb, 0x7e, 0xd8, 0x07, 0xd9, 0xf3, 0x49, 0x38, 0xbd, 0x89, 0x79, 0xee, 0x02, 0x39, - 0x9e, 0x74, 0x45, 0x49, 0x85, 0xe1, 0x0c, 0xee, 0xc7, 0x1a, 0x9e, 0x08, 0xf2, 0xaa, 0xf9, 0x35, - 0x23, 0xfd, 0x52, 0x44, 0x65, 0xe2, 0xaf, 0x80, 0x2c, 0xb9, 0x79, 0x26, 0x3c, 0x23, 0xf5, 0x12, - 0x1e, 0x92, 0xd2, 0x2b, 0x0a, 0x86, 0xe8, 0x38, 0x7e, 0x0a, 0x60, 0x59, 0x95, 0x21, 0x31, 0x2d, - 0x88, 0x52, 0x98, 0xfd, 0x97, 0x77, 0x32, 0xce, 0x99, 0x61, 0x4e, 0xa6, 0x5f, 0x97, 0x37, 0x4b, - 0xf4, 0xf4, 0xfe, 0x69, 0x66, 0xdf, 0x93, 0x37, 0xd1, 0x50, 0x35, 0xd9, 0xd0, 0x7b, 0xb0, 0x38, - 0x68, 0x28, 0x36, 0xf4, 0xe5, 0x8c, 0xcd, 0xec, 0x0f, 0x3a, 0x29, 0xe0, 0xb8, 0x82, 0x3e, 0x3f, - 0x4a, 0x37, 0x9a, 0xf5, 0x11, 0xbc, 0x30, 0x32, 0x2a, 0xb1, 0x6e, 0xc2, 0x73, 0x49, 0xac, 0xfe, - 0x90, 0x27, 0xe0, 0x2a, 0x26, 0xb8, 0x98, 0x3e, 0x0b, 0x11, 0x97, 0xde, 0xc3, 0x3e, 0xee, 0x44, - 0x40, 0xbb, 0xf2, 0xaa, 0xec, 0xaf, 0x4a, 0x90, 0x6b, 0x70, 0xca, 0xe3, 0x2b, 0x72, 0x2e, 0x73, - 0x69, 0x7d, 0xf1, 0x84, 0x14, 0x93, 0xd9, 0x6b, 0xbf, 0x9e, 0x83, 0x8f, 0xf1, 0x7a, 0xe8, 0x2b, - 0x00, 0xa7, 0xe3, 0x64, 0xe8, 0x6a, 0xba, 0x84, 0xca, 0x2f, 0xb5, 0xe5, 0x4c, 0xb9, 0x82, 0x55, - 0x5f, 0xb9, 0xfb, 0xdb, 0xdf, 0x5f, 0x3e, 0xb2, 0x84, 0x2e, 0x99, 0xc3, 0x06, 0x2e, 0x3c, 0x3a, - 0x61, 0x39, 0xe8, 0x1b, 0x00, 0x4b, 0x09, 0x07, 0xf9, 0x00, 0x7b, 0xff, 0x1f, 0x5b, 0x95, 0xb3, - 0x2d, 0xa3, 0x2b, 0x59, 0xd8, 0x1a, 0x41, 0xc8, 0xf2, 0x2d, 0x80, 0x85, 0x84, 0x7d, 0xa2, 0x2c, - 0x8a, 0xfd, 0x0d, 0xd5, 0x56, 0xb2, 0x25, 0x4b, 0xbe, 0x75, 0xce, 0xb7, 0x8a, 0x96, 0x15, 0x7c, - 0xe1, 0xcf, 0x0d, 0x96, 0xa4, 0x64, 0xe8, 0x33, 0x00, 0xcf, 0x4a, 0x0f, 0x45, 0x8b, 0x0a, 0xb9, - 0xa4, 0xf5, 0x6a, 0x4b, 0xe3, 0xd2, 0x32, 0xee, 0xa5, 0xe0, 0x91, 0x1e, 0x8b, 0xbe, 0x06, 0x30, - 0x1f, 0x33, 0x51, 0x74, 0x45, 0xa1, 0x92, 0xf6, 0x60, 0xed, 0x6a, 0x96, 0xd4, 0x8c, 0x9b, 0x28, - 0xa0, 0xe2, 0xb6, 0x8d, 0x7e, 0x06, 0xb0, 0x34, 0xec, 0x89, 0xc8, 0x50, 0x68, 0x2a, 0xdc, 0x58, - 0x33, 0x33, 0xe7, 0x4b, 0xd0, 0x1a, 0x07, 0x7d, 0x09, 0x6d, 0x28, 0x40, 0xa3, 0xbb, 0x92, 0x99, - 0x1f, 0x26, 0x6f, 0xd3, 0x8f, 0x4d, 0x61, 0xc9, 0xe8, 0x3b, 0x00, 0xf3, 0x31, 0xfb, 0x54, 0x8e, - 0x34, 0x6d, 0xd7, 0xca, 0x91, 0x8e, 0x70, 0x63, 0xfd, 0x55, 0x4e, 0xba, 0x81, 0x5e, 0x98, 0x80, - 0x34, 0xb4, 0x6c, 0xf4, 0x0b, 0x80, 0xa5, 0x61, 0xbf, 0x52, 0x0e, 0x58, 0x61, 0xe8, 0xca, 0x01, - 0xab, 0xec, 0x5a, 0xbf, 0xce, 0xb1, 0x77, 0xd0, 0xf6, 0x04, 0xd8, 0x29, 0x03, 0x45, 0x3f, 0x02, - 0x38, 0x93, 0xf2, 0x5c, 0x94, 0x15, 0x2a, 0x3a, 0xca, 0xcf, 0x65, 0x7f, 0x40, 0xb6, 0xf1, 0x32, - 0x6f, 0xe3, 0x1a, 0x7a, 0x7e, 0x7c, 0x1b, 0x69, 0xdb, 0x47, 0x3f, 0x01, 0x58, 0x48, 0xf8, 0x97, - 0xf2, 0x82, 0x1a, 0xe5, 0xe4, 0xca, 0x0b, 0x6a, 0xa4, 0x51, 0xeb, 0x6f, 0x70, 0xd4, 0x2d, 0x54, - 0x53, 0xa3, 0xb6, 0x9c, 0xb1, 0x13, 0xe7, 0xe3, 0xfe, 0x1e, 0xc0, 0x62, 0xd2, 0x77, 0x51, 0x26, - 0x96, 0x68, 0xd0, 0xab, 0x19, 0xb3, 0x25, 0xfa, 0x06, 0x47, 0x5f, 0x47, 0xd5, 0xff, 0x32, 0x65, - 0x31, 0xe2, 0x8f, 0xe0, 0x94, 0xb0, 0x57, 0x74, 0x49, 0xa1, 0x99, 0x70, 0x71, 0x6d, 0x71, 0x4c, - 0x96, 0x24, 0x5a, 0xe4, 0x44, 0x15, 0xb4, 0xa0, 0xbc, 0xc8, 0xb8, 0xa5, 0xef, 0xdc, 0x3f, 0x2e, - 0x83, 0x07, 0xc7, 0x65, 0xf0, 0xd7, 0x71, 0x19, 0x7c, 0x71, 0x52, 0xce, 0x3d, 0x38, 0x29, 0xe7, - 0x7e, 0x3f, 0x29, 0xe7, 0x6e, 0xae, 0x8c, 0xfb, 0xfe, 0x90, 0x05, 0xf9, 0xc7, 0x63, 0x73, 0x8a, - 0x7f, 0x18, 0xaf, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x10, 0x7a, 0x57, 0xeb, 0xbd, 0x0f, 0x00, - 0x00, + 0x1b, 0xc7, 0x3d, 0x4d, 0xde, 0xf4, 0xe5, 0x71, 0x9c, 0x3a, 0x43, 0x10, 0xae, 0x9b, 0xd8, 0xe9, + 0xd2, 0x44, 0x69, 0x93, 0xec, 0x92, 0xa4, 0x2a, 0x0a, 0x14, 0x41, 0x7e, 0x50, 0x51, 0xd4, 0x40, + 0x30, 0x51, 0x84, 0x7a, 0xb1, 0xc6, 0xeb, 0xe9, 0x66, 0x55, 0x7b, 0xd7, 0xdd, 0x19, 0x9b, 0x46, + 0x85, 0x4b, 0x05, 0x88, 0x23, 0x12, 0x42, 0xdc, 0xa0, 0x17, 0x24, 0xc4, 0x19, 0xb8, 0x97, 0x53, + 0x8f, 0x95, 0xb8, 0x20, 0x0e, 0x05, 0x25, 0x1c, 0xf8, 0x33, 0xd0, 0xce, 0x8e, 0xd7, 0xbb, 0x5e, + 0x8f, 0xb2, 0xb8, 0x70, 0x4a, 0x34, 0xcf, 0xb3, 0xcf, 0xf3, 0x79, 0xbe, 0x3b, 0x33, 0x5f, 0x2f, + 0x4c, 0x3b, 0x76, 0xcd, 0xf6, 0xda, 0x86, 0xeb, 0x11, 0xb3, 0x41, 0x8d, 0xce, 0x8a, 0x71, 0xa7, + 0x4d, 0xbd, 0x43, 0xbd, 0xe5, 0xb9, 0xdc, 0xc5, 0xf9, 0x20, 0xaa, 0x07, 0x51, 0xbd, 0xb3, 0x52, + 0x9c, 0xb2, 0x5c, 0xcb, 0x15, 0x41, 0xc3, 0xff, 0x2f, 0xc8, 0x2b, 0x4e, 0x5b, 0xae, 0x6b, 0x35, + 0xa8, 0x41, 0x5a, 0xb6, 0x41, 0x1c, 0xc7, 0xe5, 0x84, 0xdb, 0xae, 0xc3, 0x64, 0x74, 0x26, 0xd1, + 0x43, 0xd6, 0x0b, 0xc2, 0x25, 0xd3, 0x65, 0x4d, 0x97, 0x19, 0x35, 0xc2, 0xfc, 0x60, 0x8d, 0x72, + 0xb2, 0x62, 0x98, 0xae, 0xed, 0x04, 0x71, 0xad, 0x03, 0x85, 0x77, 0x7d, 0xa6, 0x37, 0xee, 0x9a, + 0x07, 0xc4, 0xb1, 0x68, 0x85, 0x70, 0x5a, 0xa1, 0x77, 0xda, 0x94, 0x71, 0xbc, 0x0b, 0xa3, 0x2d, + 0x62, 0x7b, 0x05, 0x34, 0x8b, 0x16, 0x9e, 0xd9, 0xbc, 0xfa, 0xe8, 0x49, 0x39, 0xf3, 0xdb, 0x93, + 0xf2, 0x65, 0xcb, 0xe6, 0x07, 0xed, 0x9a, 0x6e, 0xba, 0x4d, 0xe3, 0x6d, 0xd1, 0x7b, 0xeb, 0x80, + 0xd8, 0x8e, 0x21, 0x39, 0x3a, 0xab, 0xc6, 0x5d, 0xc3, 0x74, 0x9b, 0x4d, 0xd7, 0x31, 0x08, 0x63, + 0x94, 0xeb, 0xbb, 0xc4, 0xf6, 0x2a, 0xa2, 0xd2, 0xcb, 0xff, 0xff, 0xec, 0x41, 0x39, 0xf3, 0xd7, + 0x83, 0x72, 0x46, 0x7b, 0x88, 0xe0, 0xec, 0x80, 0xc6, 0xac, 0xe5, 0x3a, 0x8c, 0xe2, 0xf7, 0x20, + 0x47, 0xe5, 0x7a, 0xd5, 0x23, 0x9c, 0x4a, 0x04, 0x5d, 0x22, 0xcc, 0x47, 0x10, 0xe4, 0x7c, 0xc1, + 0x9f, 0x65, 0x56, 0xbf, 0x6d, 0xf0, 0xc3, 0x16, 0x65, 0xfa, 0x36, 0x35, 0x2b, 0xe3, 0x34, 0x52, + 0x1c, 0x2f, 0x01, 0xae, 0x35, 0x5c, 0xf3, 0x76, 0x95, 0xdb, 0x4d, 0xca, 0x38, 0x69, 0xb6, 0xaa, + 0x4d, 0x56, 0x38, 0x35, 0x8b, 0x16, 0x46, 0x2a, 0x79, 0x11, 0xd9, 0xeb, 0x06, 0x76, 0x18, 0x3e, + 0x0f, 0xe3, 0x41, 0xf6, 0x01, 0xb5, 0xad, 0x03, 0x5e, 0x18, 0x99, 0x45, 0x0b, 0xa3, 0x95, 0xac, + 0x58, 0x7b, 0x53, 0x2c, 0x69, 0xe7, 0x06, 0x8c, 0xc0, 0xa4, 0x78, 0xda, 0xc7, 0x08, 0x8a, 0x83, + 0xa2, 0x72, 0xc2, 0x5b, 0x30, 0x11, 0x9b, 0x90, 0x15, 0xd0, 0xec, 0xc8, 0x42, 0x76, 0xf5, 0x05, + 0xbd, 0x7f, 0x57, 0xe8, 0xd1, 0x02, 0x7b, 0xed, 0x56, 0x83, 0x6e, 0x16, 0x7d, 0x1d, 0xbe, 0xff, + 0xbd, 0x8c, 0x13, 0x21, 0x56, 0xc9, 0x45, 0x67, 0x66, 0xda, 0x73, 0xf0, 0xac, 0xa0, 0xd8, 0x30, + 0xb9, 0xdd, 0xe9, 0xd1, 0x39, 0x30, 0x15, 0x5f, 0x96, 0x58, 0xfb, 0x70, 0x9a, 0x04, 0x4b, 0x82, + 0xe7, 0x69, 0xdf, 0x7a, 0xb7, 0x98, 0x76, 0x16, 0x9e, 0x17, 0xfd, 0xf6, 0x5d, 0x4e, 0xf7, 0x88, + 0x67, 0x51, 0x1e, 0xa2, 0xdc, 0x93, 0x3b, 0x30, 0x16, 0x92, 0x38, 0x55, 0x18, 0xef, 0xb8, 0x9c, + 0x56, 0x79, 0xb0, 0xfe, 0xaf, 0x30, 0x65, 0x3b, 0xbd, 0x46, 0xda, 0x3b, 0x30, 0x2d, 0x9a, 0x5f, + 0xa3, 0xb4, 0x4e, 0xbd, 0x6d, 0xda, 0xa0, 0x96, 0x38, 0x5d, 0xdd, 0x23, 0x30, 0x07, 0x13, 0x1d, + 0xd2, 0xb0, 0xeb, 0x84, 0xbb, 0x5e, 0x95, 0xd4, 0xeb, 0xf2, 0x30, 0x54, 0x72, 0xe1, 0xea, 0x46, + 0xbd, 0x1e, 0xdd, 0xd7, 0xaf, 0xc3, 0x8c, 0xa2, 0xa0, 0x1c, 0xa9, 0x0c, 0xd9, 0x5b, 0x22, 0x16, + 0x2d, 0x07, 0xc1, 0x92, 0x5f, 0x4b, 0x7b, 0x4b, 0x4a, 0xb5, 0x63, 0x33, 0xb6, 0xe5, 0xb6, 0x1d, + 0x4e, 0xbd, 0xa1, 0x69, 0x5e, 0x95, 0xda, 0xc6, 0x6a, 0x49, 0x90, 0xf3, 0x30, 0xde, 0xb4, 0x19, + 0xab, 0x9a, 0xc1, 0xba, 0x28, 0x35, 0x5a, 0xc9, 0x36, 0x7b, 0xa9, 0xa1, 0x3a, 0x1b, 0x96, 0xe5, + 0xf9, 0x73, 0xd0, 0x5d, 0x8f, 0xfa, 0xea, 0x0d, 0xcd, 0x73, 0x1f, 0x49, 0x79, 0x92, 0x15, 0x25, + 0x15, 0x81, 0x49, 0xd2, 0x8d, 0x55, 0x5b, 0x41, 0x50, 0x54, 0xcd, 0xae, 0xea, 0xc9, 0xa3, 0x11, + 0x96, 0x89, 0x1e, 0x04, 0x59, 0x72, 0x73, 0xd4, 0xdf, 0x26, 0x95, 0x3c, 0xe9, 0x6b, 0xa5, 0x95, + 0x15, 0x0c, 0xe1, 0x8e, 0xfc, 0x04, 0x41, 0x49, 0x95, 0x21, 0x31, 0x4d, 0xc0, 0x09, 0xcc, 0xee, + 0x11, 0x1e, 0x8e, 0x73, 0xb2, 0x9f, 0x93, 0x69, 0x37, 0xe4, 0xfd, 0x12, 0x3e, 0xbd, 0xff, 0x34, + 0xda, 0x77, 0xe4, 0x7d, 0xd4, 0x57, 0x4d, 0x0e, 0xf4, 0x3e, 0x4c, 0xf4, 0x06, 0x8a, 0x88, 0xbe, + 0x98, 0x72, 0x98, 0xfd, 0xde, 0x24, 0x39, 0x12, 0xed, 0xa0, 0x4d, 0x0f, 0xea, 0x1b, 0x6a, 0x7d, + 0x08, 0xe7, 0x06, 0x46, 0x25, 0xd6, 0x4d, 0x38, 0x13, 0xc7, 0xea, 0x8a, 0x3c, 0x04, 0xd7, 0x44, + 0x8c, 0x8b, 0x69, 0x53, 0x80, 0x45, 0xeb, 0x5d, 0xe2, 0x91, 0x66, 0x08, 0xb4, 0x23, 0x2f, 0xcc, + 0xee, 0xaa, 0x04, 0xb9, 0x02, 0x63, 0x2d, 0xb1, 0x22, 0x75, 0x29, 0x24, 0xfb, 0x07, 0x4f, 0xc8, + 0x66, 0x32, 0x7b, 0xf5, 0xe7, 0x33, 0xf0, 0x3f, 0x51, 0x0f, 0x7f, 0x89, 0x60, 0x3c, 0x4a, 0x86, + 0x2f, 0x25, 0x4b, 0xa8, 0xac, 0xb8, 0xb8, 0x98, 0x2a, 0x37, 0x60, 0xd5, 0x96, 0xee, 0xff, 0xf2, + 0xe7, 0x17, 0xa7, 0xe6, 0xf1, 0x05, 0xa3, 0xff, 0xb7, 0x41, 0x60, 0xff, 0x31, 0xe3, 0xc1, 0x5f, + 0x23, 0xc8, 0xc7, 0x7c, 0xe4, 0x03, 0xd2, 0xfa, 0xef, 0xd8, 0x56, 0x04, 0xdb, 0x22, 0xbe, 0x98, + 0x86, 0xad, 0xca, 0x7d, 0x96, 0x6f, 0x10, 0xe4, 0x62, 0x26, 0x8a, 0xd3, 0x74, 0xec, 0xbe, 0xd0, + 0xe2, 0x52, 0xba, 0x64, 0xc9, 0xb7, 0x26, 0xf8, 0x96, 0xf1, 0xa2, 0x82, 0xcf, 0xff, 0x19, 0xc3, + 0xe2, 0x94, 0x0c, 0x7f, 0x8a, 0xe0, 0xb4, 0x74, 0x52, 0x3c, 0xa7, 0x68, 0x17, 0x37, 0xe0, 0xe2, + 0xfc, 0x49, 0x69, 0x29, 0xdf, 0x65, 0xc0, 0x23, 0x6d, 0x16, 0x7f, 0x85, 0x20, 0x1b, 0xf1, 0x51, + 0x7c, 0x51, 0xd1, 0x25, 0x69, 0xc3, 0xc5, 0x4b, 0x69, 0x52, 0x53, 0xbe, 0xc4, 0x00, 0x2a, 0xea, + 0xdc, 0xf8, 0x27, 0x04, 0xf9, 0x7e, 0x4f, 0xc4, 0xba, 0xa2, 0xa7, 0xc2, 0x8d, 0x8b, 0x46, 0xea, + 0x7c, 0x09, 0xba, 0x21, 0x40, 0x5f, 0xc1, 0xeb, 0x0a, 0xd0, 0xf0, 0xae, 0x64, 0xc6, 0xbd, 0xf8, + 0x6d, 0xfa, 0x91, 0x11, 0x58, 0x32, 0xfe, 0x16, 0x41, 0x36, 0x62, 0x9f, 0x4a, 0x49, 0x93, 0x76, + 0xad, 0x94, 0x74, 0x80, 0x1b, 0x6b, 0xaf, 0x09, 0xd2, 0x75, 0xfc, 0xd2, 0x10, 0xa4, 0xbe, 0x65, + 0xe3, 0x87, 0x08, 0xf2, 0xfd, 0x7e, 0xa5, 0x14, 0x58, 0x61, 0xe8, 0x4a, 0x81, 0x55, 0x76, 0xad, + 0xdd, 0x10, 0xd8, 0xd7, 0xf0, 0xf6, 0x10, 0xd8, 0x09, 0x03, 0xc5, 0x3f, 0x20, 0x98, 0x4c, 0x78, + 0x2e, 0x4e, 0x0b, 0x15, 0x6e, 0xe5, 0x17, 0xd3, 0x3f, 0x20, 0xc7, 0xb8, 0x2a, 0xc6, 0xb8, 0x82, + 0x2f, 0x9f, 0x3c, 0x46, 0xd2, 0xf6, 0xf1, 0x8f, 0x08, 0x72, 0x31, 0xff, 0x52, 0x5e, 0x50, 0x83, + 0x9c, 0x5c, 0x79, 0x41, 0x0d, 0x34, 0x6a, 0xed, 0xba, 0x40, 0xdd, 0xc2, 0x1b, 0x6a, 0xd4, 0xba, + 0x7d, 0xa2, 0xe2, 0x42, 0xee, 0xef, 0x10, 0x4c, 0xc4, 0x7d, 0x17, 0xa7, 0x62, 0x09, 0x85, 0x5e, + 0x4e, 0x99, 0x2d, 0xd1, 0xd7, 0x05, 0xfa, 0x1a, 0x5e, 0xf9, 0x27, 0x2a, 0x07, 0x12, 0x7f, 0x08, + 0x63, 0x81, 0xbd, 0xe2, 0x0b, 0x8a, 0x9e, 0x31, 0x17, 0x2f, 0xce, 0x9d, 0x90, 0x25, 0x89, 0xe6, + 0x04, 0x51, 0x19, 0xcf, 0x28, 0x2f, 0x32, 0x61, 0xe9, 0xd7, 0x1f, 0x1d, 0x95, 0xd0, 0xe3, 0xa3, + 0x12, 0xfa, 0xe3, 0xa8, 0x84, 0x3e, 0x3f, 0x2e, 0x65, 0x1e, 0x1f, 0x97, 0x32, 0xbf, 0x1e, 0x97, + 0x32, 0x37, 0x8d, 0x14, 0x9f, 0x20, 0xb2, 0xa6, 0xf8, 0x2c, 0xad, 0x8d, 0x89, 0xcf, 0xee, 0xb5, + 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x14, 0xd6, 0x3e, 0x1e, 0x1b, 0x10, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1072,7 +1095,8 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { - // ExchangeRate returns exchange rate of a pair + // ExchangeRate returns exchange rate of a pair along with the block height and + // block time that the exchange rate was set by the oracle module. ExchangeRate(ctx context.Context, in *QueryExchangeRateRequest, opts ...grpc.CallOption) (*QueryExchangeRateResponse, error) // ExchangeRateTwap returns twap exchange rate of a pair ExchangeRateTwap(ctx context.Context, in *QueryExchangeRateRequest, opts ...grpc.CallOption) (*QueryExchangeRateResponse, error) @@ -1216,7 +1240,8 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts . // QueryServer is the server API for Query service. type QueryServer interface { - // ExchangeRate returns exchange rate of a pair + // ExchangeRate returns exchange rate of a pair along with the block height and + // block time that the exchange rate was set by the oracle module. ExchangeRate(context.Context, *QueryExchangeRateRequest) (*QueryExchangeRateResponse, error) // ExchangeRateTwap returns twap exchange rate of a pair ExchangeRateTwap(context.Context, *QueryExchangeRateRequest) (*QueryExchangeRateResponse, error) @@ -1613,6 +1638,16 @@ func (m *QueryExchangeRateResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + if m.BlockHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x18 + } + if m.BlockTimestampMs != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockTimestampMs)) + i-- + dAtA[i] = 0x10 + } { size := m.ExchangeRate.Size() i -= size @@ -2256,6 +2291,12 @@ func (m *QueryExchangeRateResponse) Size() (n int) { _ = l l = m.ExchangeRate.Size() n += 1 + l + sovQuery(uint64(l)) + if m.BlockTimestampMs != 0 { + n += 1 + sovQuery(uint64(m.BlockTimestampMs)) + } + if m.BlockHeight != 0 { + n += 1 + sovQuery(uint64(m.BlockHeight)) + } return n } @@ -2651,6 +2692,44 @@ func (m *QueryExchangeRateResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockTimestampMs", wireType) + } + m.BlockTimestampMs = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockTimestampMs |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2915,7 +2994,7 @@ func (m *QueryActivesResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - var v github_com_NibiruChain_nibiru_x_common_asset.Pair + var v github_com_NibiruChain_nibiru_v2_x_common_asset.Pair m.Actives = append(m.Actives, v) if err := m.Actives[len(m.Actives)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -3051,7 +3130,7 @@ func (m *QueryVoteTargetsResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - var v github_com_NibiruChain_nibiru_x_common_asset.Pair + var v github_com_NibiruChain_nibiru_v2_x_common_asset.Pair m.VoteTargets = append(m.VoteTargets, v) if err := m.VoteTargets[len(m.VoteTargets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err diff --git a/x/oracle/types/state.pb.go b/x/oracle/types/state.pb.go index 1569bbca1..e0c999aad 100644 --- a/x/oracle/types/state.pb.go +++ b/x/oracle/types/state.pb.go @@ -5,7 +5,7 @@ package types import ( fmt "fmt" - github_com_NibiruChain_nibiru_x_common_asset "github.com/NibiruChain/nibiru/x/common/asset" + github_com_NibiruChain_nibiru_v2_x_common_asset "github.com/NibiruChain/nibiru/v2/x/common/asset" _ "github.com/cosmos/cosmos-sdk/types" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" @@ -29,8 +29,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // a snapshot of the prices at a given point in time type PriceSnapshot struct { - Pair github_com_NibiruChain_nibiru_x_common_asset.Pair `protobuf:"bytes,1,opt,name=pair,proto3,customtype=github.com/NibiruChain/nibiru/x/common/asset.Pair" json:"pair" yaml:"pair"` - Price github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=price,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"price"` + Pair github_com_NibiruChain_nibiru_v2_x_common_asset.Pair `protobuf:"bytes,1,opt,name=pair,proto3,customtype=github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" json:"pair" yaml:"pair"` + Price github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=price,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"price"` // milliseconds since unix epoch TimestampMs int64 `protobuf:"varint,3,opt,name=timestamp_ms,json=timestampMs,proto3" json:"timestamp_ms,omitempty"` } @@ -82,28 +82,28 @@ func init() { func init() { proto.RegisterFile("nibiru/oracle/v1/state.proto", fileDescriptor_125e6c5a6e45c0d0) } var fileDescriptor_125e6c5a6e45c0d0 = []byte{ - // 329 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0x41, 0x4b, 0xf3, 0x30, - 0x18, 0x80, 0xdb, 0x6f, 0x9f, 0x82, 0x9d, 0x82, 0x14, 0x0f, 0x63, 0xcc, 0x6c, 0xee, 0x20, 0x3b, - 0x68, 0x42, 0xf1, 0xe6, 0x71, 0x0e, 0xf1, 0xa2, 0x8c, 0x79, 0x13, 0x41, 0xde, 0xc6, 0xd0, 0x05, - 0x97, 0xbc, 0xa1, 0xc9, 0x86, 0xfb, 0x17, 0xfe, 0xac, 0x1d, 0x77, 0x12, 0xf1, 0x30, 0x64, 0xfb, - 0x07, 0xfe, 0x02, 0x69, 0x53, 0x44, 0xf0, 0xe0, 0xa9, 0x25, 0x4f, 0xf2, 0x3c, 0xc9, 0x1b, 0xb5, - 0xb4, 0x4c, 0x65, 0x3e, 0x65, 0x98, 0x03, 0x9f, 0x08, 0x36, 0x4b, 0x98, 0x75, 0xe0, 0x04, 0x35, - 0x39, 0x3a, 0x8c, 0xf7, 0x3d, 0xa5, 0x9e, 0xd2, 0x59, 0xd2, 0x3c, 0xc8, 0x30, 0xc3, 0x12, 0xb2, - 0xe2, 0xcf, 0xef, 0x6b, 0xb6, 0x32, 0xc4, 0x6c, 0x22, 0x18, 0x18, 0xc9, 0x40, 0x6b, 0x74, 0xe0, - 0x24, 0x6a, 0x5b, 0xd1, 0xc3, 0x5f, 0x8d, 0xca, 0xe7, 0x31, 0xe1, 0x68, 0x15, 0x5a, 0x96, 0x82, - 0x2d, 0x60, 0x2a, 0x1c, 0x24, 0x8c, 0xa3, 0xd4, 0x9e, 0x77, 0x5f, 0xc3, 0x68, 0x6f, 0x98, 0x4b, - 0x2e, 0x6e, 0x35, 0x18, 0x3b, 0x46, 0x17, 0xdf, 0x47, 0xff, 0x0d, 0xc8, 0xbc, 0x11, 0x76, 0xc2, - 0xde, 0x4e, 0xff, 0x6a, 0xb1, 0x6a, 0x07, 0xef, 0xab, 0x76, 0x92, 0x49, 0x37, 0x9e, 0xa6, 0x94, - 0xa3, 0x62, 0x37, 0x65, 0xf1, 0x62, 0x0c, 0x52, 0xb3, 0xaa, 0xfe, 0xcc, 0x38, 0x2a, 0x85, 0x9a, - 0x81, 0xb5, 0xc2, 0xd1, 0x21, 0xc8, 0xfc, 0x73, 0xd5, 0xae, 0xcf, 0x41, 0x4d, 0xce, 0xbb, 0x85, - 0xae, 0x3b, 0x2a, 0xad, 0xf1, 0x20, 0xda, 0x32, 0x45, 0xae, 0xf1, 0xaf, 0xd4, 0xd3, 0x4a, 0x7f, - 0xfc, 0x43, 0x5f, 0xdd, 0xd8, 0x7f, 0x4e, 0xed, 0xe3, 0x13, 0x73, 0x73, 0x23, 0x2c, 0x1d, 0x08, - 0x3e, 0xf2, 0x87, 0xe3, 0xa3, 0x68, 0xd7, 0x49, 0x25, 0xac, 0x03, 0x65, 0x1e, 0x94, 0x6d, 0xd4, - 0x3a, 0x61, 0xaf, 0x36, 0xaa, 0x7f, 0xaf, 0x5d, 0xdb, 0xfe, 0xe5, 0x62, 0x4d, 0xc2, 0xe5, 0x9a, - 0x84, 0x1f, 0x6b, 0x12, 0xbe, 0x6c, 0x48, 0xb0, 0xdc, 0x90, 0xe0, 0x6d, 0x43, 0x82, 0xbb, 0x93, - 0xbf, 0x9e, 0x52, 0x8d, 0xb2, 0xac, 0xa6, 0xdb, 0xe5, 0x9c, 0xce, 0xbe, 0x02, 0x00, 0x00, 0xff, - 0xff, 0x21, 0xeb, 0xd1, 0x23, 0xcc, 0x01, 0x00, 0x00, + // 330 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x90, 0xb1, 0x4a, 0x03, 0x31, + 0x18, 0xc7, 0xef, 0xac, 0x0a, 0x5e, 0x15, 0xe4, 0x70, 0x28, 0xa5, 0x5e, 0x6b, 0x07, 0xe9, 0xe2, + 0x7d, 0x54, 0x9d, 0x1c, 0x6b, 0x17, 0x11, 0xa5, 0xd4, 0xcd, 0xa5, 0x7c, 0x17, 0xc3, 0x35, 0xd8, + 0xe4, 0x0b, 0x97, 0xb4, 0xd8, 0xb7, 0xf0, 0xb1, 0x3a, 0x76, 0x54, 0x87, 0x22, 0xed, 0x1b, 0xf8, + 0x04, 0x72, 0x97, 0x43, 0x04, 0x17, 0xa7, 0x84, 0xfc, 0x92, 0xff, 0x2f, 0xdf, 0x3f, 0x68, 0x28, + 0x91, 0x88, 0x6c, 0x0a, 0x94, 0x21, 0x9b, 0x70, 0x98, 0x75, 0xc1, 0x58, 0xb4, 0x3c, 0xd6, 0x19, + 0x59, 0x0a, 0x0f, 0x1d, 0x8d, 0x1d, 0x8d, 0x67, 0xdd, 0xfa, 0x51, 0x4a, 0x29, 0x15, 0x10, 0xf2, + 0x9d, 0xbb, 0x57, 0x6f, 0xa4, 0x44, 0xe9, 0x84, 0x03, 0x6a, 0x01, 0xa8, 0x14, 0x59, 0xb4, 0x82, + 0x94, 0x29, 0xe9, 0xf1, 0x1f, 0x47, 0x99, 0xe7, 0x70, 0xc4, 0xc8, 0x48, 0x32, 0x90, 0xa0, 0xc9, + 0x61, 0xc2, 0x2d, 0x76, 0x81, 0x91, 0x50, 0x8e, 0xb7, 0xdf, 0xfd, 0xe0, 0x60, 0x90, 0x09, 0xc6, + 0x1f, 0x14, 0x6a, 0x33, 0x26, 0x1b, 0x8e, 0x82, 0x6d, 0x8d, 0x22, 0xab, 0xf9, 0x2d, 0xbf, 0xb3, + 0xd7, 0xbb, 0x5d, 0xac, 0x9a, 0xde, 0xc7, 0xaa, 0x79, 0x99, 0x0a, 0x3b, 0x9e, 0x26, 0x31, 0x23, + 0x09, 0xf7, 0x85, 0xf1, 0x7a, 0x8c, 0x42, 0x41, 0x69, 0x9f, 0x9d, 0xc3, 0x0b, 0x30, 0x92, 0x92, + 0x14, 0xa0, 0x31, 0xdc, 0xc6, 0x03, 0x14, 0xd9, 0xd7, 0xaa, 0x59, 0x9d, 0xa3, 0x9c, 0x5c, 0xb5, + 0xf3, 0xc4, 0xf6, 0xb0, 0x08, 0x0e, 0xfb, 0xc1, 0x8e, 0xce, 0x8d, 0xb5, 0xad, 0xc2, 0x10, 0x97, + 0x86, 0xd3, 0x5f, 0x86, 0xf2, 0xd3, 0x6e, 0x39, 0x33, 0x4f, 0xcf, 0x60, 0xe7, 0x9a, 0x9b, 0xb8, + 0xcf, 0xd9, 0xd0, 0x3d, 0x0e, 0x4f, 0x82, 0x7d, 0x2b, 0x24, 0x37, 0x16, 0xa5, 0x1e, 0x49, 0x53, + 0xab, 0xb4, 0xfc, 0x4e, 0x65, 0x58, 0xfd, 0x39, 0xbb, 0x33, 0xbd, 0x9b, 0xc5, 0x3a, 0xf2, 0x97, + 0xeb, 0xc8, 0xff, 0x5c, 0x47, 0xfe, 0xeb, 0x26, 0xf2, 0x96, 0x9b, 0xc8, 0x7b, 0xdb, 0x44, 0xde, + 0x23, 0xfc, 0x63, 0x9a, 0xb2, 0xd0, 0x42, 0x9c, 0xec, 0x16, 0x6d, 0x5d, 0x7c, 0x07, 0x00, 0x00, + 0xff, 0xff, 0x75, 0xe2, 0x1a, 0x07, 0xd2, 0x01, 0x00, 0x00, } func (m *PriceSnapshot) Marshal() (dAtA []byte, err error) { diff --git a/x/oracle/types/test_utils.go b/x/oracle/types/test_utils.go index 67b51d075..c5e06e88f 100644 --- a/x/oracle/types/test_utils.go +++ b/x/oracle/types/test_utils.go @@ -78,7 +78,7 @@ func (sk DummyStakingKeeper) Validator(ctx sdk.Context, address sdk.ValAddress) // TotalBondedTokens nolint func (DummyStakingKeeper) TotalBondedTokens(_ sdk.Context) sdk.Int { - return sdk.ZeroInt() + return sdkmath.ZeroInt() } // Slash nolint @@ -140,15 +140,18 @@ func (v MockValidator) GetBondedTokens() sdk.Int { } func (v MockValidator) GetConsensusPower(powerReduction sdk.Int) int64 { return v.power } func (v *MockValidator) SetConsensusPower(power int64) { v.power = power } -func (v MockValidator) GetCommission() sdk.Dec { return sdk.ZeroDec() } -func (v MockValidator) GetMinSelfDelegation() sdk.Int { return sdk.OneInt() } -func (v MockValidator) GetDelegatorShares() sdk.Dec { return sdk.NewDec(v.power) } -func (v MockValidator) TokensFromShares(sdk.Dec) sdk.Dec { return sdk.ZeroDec() } -func (v MockValidator) TokensFromSharesTruncated(sdk.Dec) sdk.Dec { return sdk.ZeroDec() } -func (v MockValidator) TokensFromSharesRoundUp(sdk.Dec) sdk.Dec { return sdk.ZeroDec() } -func (v MockValidator) SharesFromTokens(amt sdk.Int) (sdk.Dec, error) { return sdk.ZeroDec(), nil } +func (v MockValidator) GetCommission() sdk.Dec { return sdkmath.LegacyZeroDec() } +func (v MockValidator) GetMinSelfDelegation() sdk.Int { return sdkmath.OneInt() } +func (v MockValidator) GetDelegatorShares() sdk.Dec { return sdkmath.LegacyNewDec(v.power) } +func (v MockValidator) TokensFromShares(sdk.Dec) sdk.Dec { return sdkmath.LegacyZeroDec() } +func (v MockValidator) TokensFromSharesTruncated(sdk.Dec) sdk.Dec { return sdkmath.LegacyZeroDec() } +func (v MockValidator) TokensFromSharesRoundUp(sdk.Dec) sdk.Dec { return sdkmath.LegacyZeroDec() } +func (v MockValidator) SharesFromTokens(amt sdk.Int) (sdk.Dec, error) { + return sdkmath.LegacyZeroDec(), nil +} + func (v MockValidator) SharesFromTokensTruncated(amt sdk.Int) (sdk.Dec, error) { - return sdk.ZeroDec(), nil + return sdkmath.LegacyZeroDec(), nil } func NewMockValidator(valAddr sdk.ValAddress, power int64) MockValidator { diff --git a/x/oracle/types/tx.pb.go b/x/oracle/types/tx.pb.go index 564538efc..aac5d9c01 100644 --- a/x/oracle/types/tx.pb.go +++ b/x/oracle/types/tx.pb.go @@ -6,7 +6,7 @@ package types import ( context "context" fmt "fmt" - github_com_NibiruChain_nibiru_x_common_asset "github.com/NibiruChain/nibiru/x/common/asset" + github_com_NibiruChain_nibiru_v2_x_common_asset "github.com/NibiruChain/nibiru/v2/x/common/asset" github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" @@ -373,7 +373,7 @@ type OracleParamsMsg struct { RewardBand *github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,3,opt,name=reward_band,json=rewardBand,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"reward_band,omitempty" yaml:"reward_band"` // The set of whitelisted markets, or asset pairs, for the module. // Ex. '["unibi:uusd","ubtc:uusd"]' - Whitelist []github_com_NibiruChain_nibiru_x_common_asset.Pair `protobuf:"bytes,4,rep,name=whitelist,proto3,customtype=github.com/NibiruChain/nibiru/x/common/asset.Pair" json:"whitelist,omitempty" yaml:"whitelist"` + Whitelist []github_com_NibiruChain_nibiru_v2_x_common_asset.Pair `protobuf:"bytes,4,rep,name=whitelist,proto3,customtype=github.com/NibiruChain/nibiru/v2/x/common/asset.Pair" json:"whitelist,omitempty" yaml:"whitelist"` // SlashFraction returns the proportion of an oracle's stake that gets // slashed in the event of slashing. `SlashFraction` specifies the exact // penalty for failing a voting period. @@ -477,76 +477,76 @@ func init() { func init() { proto.RegisterFile("nibiru/oracle/v1/tx.proto", fileDescriptor_11e362c65eb610f4) } var fileDescriptor_11e362c65eb610f4 = []byte{ - // 1098 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcf, 0x6b, 0x24, 0x45, - 0x14, 0x4e, 0x6f, 0x62, 0xcc, 0xd4, 0x98, 0x4d, 0xd2, 0x93, 0xac, 0x9d, 0x49, 0xd2, 0x1d, 0x4b, - 0x8d, 0x59, 0xd8, 0x74, 0x9b, 0x08, 0xa2, 0x11, 0x44, 0x67, 0xb3, 0x11, 0x21, 0xa3, 0xa1, 0x90, - 0x15, 0xbc, 0x0c, 0x35, 0xd3, 0x95, 0x9e, 0x26, 0x3d, 0x5d, 0x4d, 0x57, 0xe5, 0x17, 0x88, 0x07, - 0x11, 0xf4, 0x28, 0x08, 0x12, 0x6f, 0xf9, 0x03, 0x04, 0xff, 0x8d, 0x3d, 0x2e, 0x78, 0x11, 0x0f, - 0xa3, 0x24, 0x1e, 0x16, 0x11, 0x0f, 0x73, 0x17, 0xa4, 0x5f, 0xd7, 0xf4, 0x74, 0x26, 0xb3, 0x9b, - 0x0c, 0x7b, 0x9a, 0xa9, 0xf7, 0x7d, 0xf5, 0xde, 0xf7, 0x5e, 0x55, 0xbd, 0xd7, 0x68, 0x3e, 0xf4, - 0xeb, 0x7e, 0x7c, 0xe0, 0xf0, 0x98, 0x36, 0x02, 0xe6, 0x1c, 0xae, 0x3b, 0xf2, 0xd8, 0x8e, 0x62, - 0x2e, 0xb9, 0x3e, 0x9d, 0x42, 0x76, 0x0a, 0xd9, 0x87, 0xeb, 0xe5, 0x59, 0x8f, 0x7b, 0x1c, 0x40, - 0x27, 0xf9, 0x97, 0xf2, 0xca, 0x8b, 0x1e, 0xe7, 0x5e, 0xc0, 0x1c, 0x1a, 0xf9, 0x0e, 0x0d, 0x43, - 0x2e, 0xa9, 0xf4, 0x79, 0x28, 0x14, 0x6a, 0x2a, 0x14, 0x56, 0xf5, 0x83, 0x3d, 0xc7, 0x3d, 0x88, - 0x81, 0xa0, 0xf0, 0xa5, 0x2b, 0x02, 0x54, 0x3c, 0x80, 0xf1, 0x2f, 0x1a, 0xb2, 0xaa, 0xc2, 0xfb, - 0xd0, 0xf3, 0x62, 0xe6, 0x51, 0xc9, 0x1e, 0x1c, 0x37, 0x9a, 0x34, 0xf4, 0x18, 0xa1, 0x92, 0xed, - 0xc6, 0xec, 0x90, 0x4b, 0xa6, 0xbf, 0x8a, 0xc6, 0x9a, 0x54, 0x34, 0x0d, 0x6d, 0x59, 0x5b, 0x2d, - 0x54, 0xa6, 0x3a, 0x6d, 0xab, 0x78, 0x42, 0x5b, 0xc1, 0x26, 0x4e, 0xac, 0x98, 0x00, 0xa8, 0xdf, - 0x45, 0xe3, 0x7b, 0x8c, 0xb9, 0x2c, 0x36, 0x6e, 0x01, 0x6d, 0xa6, 0xd3, 0xb6, 0x26, 0x53, 0x5a, - 0x6a, 0xc7, 0x44, 0x11, 0xf4, 0x0d, 0x54, 0x38, 0xa4, 0x81, 0xef, 0x52, 0xc9, 0x63, 0x63, 0x14, - 0xd8, 0xb3, 0x9d, 0xb6, 0x35, 0x9d, 0xb2, 0x33, 0x08, 0x93, 0x1e, 0x6d, 0x73, 0xe2, 0xbb, 0x33, - 0x6b, 0xe4, 0xc9, 0x99, 0x35, 0x82, 0xef, 0xa2, 0x37, 0xae, 0x11, 0x4c, 0x98, 0x88, 0x78, 0x28, - 0x18, 0xfe, 0x57, 0x43, 0x8b, 0x4f, 0xe3, 0x3e, 0x54, 0x99, 0x09, 0x1a, 0xc8, 0xab, 0x99, 0x25, - 0x56, 0x4c, 0x00, 0xd4, 0x3f, 0x40, 0xb7, 0x99, 0xda, 0x58, 0x8b, 0xa9, 0x64, 0x42, 0x65, 0x38, - 0xdf, 0x69, 0x5b, 0x73, 0x29, 0xfd, 0x32, 0x8e, 0xc9, 0x24, 0xcb, 0x45, 0x12, 0xb9, 0xda, 0x8c, - 0x0e, 0x55, 0x9b, 0xb1, 0x61, 0x6b, 0xb3, 0x82, 0x5e, 0x7b, 0x56, 0xbe, 0x59, 0x61, 0xbe, 0xd1, - 0xd0, 0x9d, 0xaa, 0xf0, 0xb6, 0x58, 0x00, 0xbc, 0x6d, 0xc6, 0xdc, 0xfb, 0x09, 0x10, 0x4a, 0xdd, - 0x41, 0x13, 0x3c, 0x62, 0x31, 0xc4, 0x4f, 0xcb, 0x52, 0xea, 0xb4, 0xad, 0xa9, 0x34, 0x7e, 0x17, - 0xc1, 0x24, 0x23, 0x25, 0x1b, 0x5c, 0xe5, 0x47, 0x15, 0x26, 0xb7, 0xa1, 0x8b, 0x60, 0x92, 0x91, - 0x72, 0x72, 0x97, 0x91, 0x39, 0x58, 0x45, 0x26, 0xf4, 0x54, 0x43, 0xa5, 0xaa, 0xf0, 0x1e, 0xb8, - 0xbe, 0xfc, 0x14, 0xae, 0xed, 0x2e, 0x8d, 0x69, 0x0b, 0x2a, 0x2a, 0x58, 0x98, 0x54, 0x54, 0xeb, - 0xaf, 0x68, 0x6a, 0xc7, 0x44, 0x11, 0xf4, 0x1d, 0x34, 0x1e, 0xc1, 0x26, 0x50, 0x57, 0xdc, 0x78, - 0xc5, 0xee, 0x7f, 0x77, 0x76, 0xde, 0x75, 0x55, 0x78, 0x79, 0x6f, 0xe9, 0x56, 0x4c, 0x94, 0x8f, - 0x9c, 0xf8, 0x25, 0xb4, 0x30, 0x40, 0x59, 0xa6, 0xfc, 0x9f, 0x09, 0x34, 0xd5, 0xe7, 0x57, 0x7f, - 0x0f, 0x15, 0x93, 0xfb, 0x59, 0x8b, 0x58, 0xec, 0x73, 0x17, 0xa4, 0x8f, 0x55, 0xca, 0x8f, 0xda, - 0x96, 0xd6, 0x69, 0x5b, 0xba, 0x3a, 0xe2, 0x1e, 0x01, 0x13, 0x94, 0xac, 0x76, 0x61, 0xa1, 0x87, - 0xe8, 0x36, 0x60, 0xb2, 0x19, 0x33, 0xd1, 0xe4, 0x81, 0xab, 0xaa, 0xfd, 0x51, 0xb2, 0xff, 0xf7, - 0xb6, 0xb5, 0xe2, 0xf9, 0xb2, 0x79, 0x50, 0xb7, 0x1b, 0xbc, 0xe5, 0x34, 0xb8, 0x68, 0x71, 0xa1, - 0x7e, 0xd6, 0x84, 0xbb, 0xef, 0xc8, 0x93, 0x88, 0x09, 0x7b, 0x8b, 0x35, 0x7a, 0x97, 0xf6, 0xb2, - 0x37, 0x4c, 0x26, 0x13, 0xc3, 0x67, 0xdd, 0xb5, 0xce, 0x50, 0x31, 0x66, 0x47, 0x34, 0x76, 0x6b, - 0x75, 0x1a, 0xba, 0xea, 0xe6, 0x6e, 0x0d, 0x1d, 0x4c, 0xa5, 0x95, 0x73, 0x85, 0x09, 0x4a, 0x57, - 0x15, 0x1a, 0xba, 0xfa, 0x3e, 0x2a, 0x1c, 0x35, 0x7d, 0xc9, 0x02, 0x5f, 0x48, 0x63, 0x6c, 0x79, - 0x74, 0xb5, 0x50, 0xa9, 0xaa, 0x20, 0xeb, 0xb9, 0x20, 0x9f, 0xc0, 0x99, 0xdd, 0x6f, 0x52, 0x3f, - 0x74, 0x54, 0x47, 0x3b, 0x76, 0x1a, 0xbc, 0xd5, 0xe2, 0xa1, 0x43, 0x85, 0x60, 0xd2, 0xde, 0xa5, - 0x7e, 0xdc, 0x7b, 0x29, 0x99, 0x4f, 0x4c, 0x7a, 0xfe, 0x93, 0x1a, 0x8a, 0x80, 0x8a, 0x66, 0x6d, - 0x2f, 0xa6, 0x8d, 0xa4, 0x49, 0x1a, 0x2f, 0x3c, 0x5f, 0x0d, 0x2f, 0x7b, 0xc3, 0x64, 0x12, 0x0c, - 0xdb, 0x6a, 0xad, 0xbf, 0x8f, 0x5e, 0x4a, 0x19, 0x47, 0x7e, 0xe8, 0xf2, 0x23, 0x63, 0x1c, 0x4e, - 0x7c, 0x41, 0x9d, 0x78, 0x29, 0xef, 0x23, 0x65, 0x60, 0x52, 0x84, 0xe5, 0xe7, 0xb0, 0xd2, 0xbf, - 0x42, 0xb3, 0x2d, 0x3f, 0xac, 0xc1, 0x53, 0x4f, 0x2e, 0x45, 0xd7, 0xcf, 0x8b, 0xa0, 0xba, 0x3a, - 0xb4, 0xea, 0x85, 0x34, 0xe2, 0x20, 0x9f, 0x98, 0xcc, 0xb4, 0xfc, 0xf0, 0x61, 0x62, 0xdd, 0x65, - 0xb1, 0x8a, 0xff, 0xa3, 0x86, 0x66, 0xe5, 0x11, 0x8d, 0x6a, 0x01, 0xe7, 0xfb, 0x75, 0xda, 0xd8, - 0xef, 0x0a, 0x98, 0x80, 0xa7, 0x34, 0x6f, 0xa7, 0xc3, 0xc7, 0xee, 0x0e, 0x1f, 0x7b, 0x4b, 0x0d, - 0x9f, 0xca, 0xc7, 0x89, 0xb6, 0xbf, 0xdb, 0x96, 0x39, 0x68, 0xfb, 0x3d, 0xde, 0xf2, 0x25, 0x6b, - 0x45, 0xf2, 0xa4, 0xa7, 0x69, 0x10, 0x0f, 0x9f, 0xfe, 0x61, 0x69, 0x44, 0x4f, 0xa0, 0x1d, 0x85, - 0x28, 0x61, 0xef, 0x20, 0x04, 0x49, 0x70, 0xc9, 0x62, 0x61, 0x14, 0xa0, 0xac, 0xf3, 0xaa, 0xac, - 0x33, 0xb9, 0x24, 0x01, 0xc7, 0xa4, 0x90, 0xa4, 0x06, 0xff, 0xf5, 0x2f, 0x51, 0x29, 0xeb, 0x9c, - 0xb5, 0x3d, 0x06, 0x2d, 0xdb, 0xe7, 0x06, 0x82, 0x8a, 0xee, 0x0c, 0x5d, 0xd1, 0x72, 0x5f, 0x63, - 0xee, 0xb9, 0xc4, 0x64, 0x26, 0xb3, 0x6e, 0xb3, 0xa4, 0x07, 0xfb, 0x5c, 0xaf, 0xa2, 0x19, 0x76, - 0x1c, 0xf9, 0x69, 0x91, 0x6a, 0xf5, 0x80, 0x37, 0xf6, 0x85, 0x51, 0x04, 0xf9, 0xcb, 0x4a, 0xbe, - 0xd1, 0x1d, 0x29, 0x7d, 0x34, 0x4c, 0xa6, 0x7b, 0xb6, 0x0a, 0x98, 0x36, 0x27, 0x4e, 0xcf, 0x2c, - 0xed, 0xc9, 0x99, 0xa5, 0x6d, 0xfc, 0x37, 0x86, 0x46, 0x93, 0x16, 0xf3, 0xb3, 0x86, 0x16, 0x9f, - 0x39, 0xcc, 0xd7, 0xaf, 0xb6, 0xbf, 0x6b, 0xc6, 0x69, 0xf9, 0xdd, 0xa1, 0xb7, 0x64, 0x5d, 0xd0, - 0xfc, 0xfa, 0xd7, 0xbf, 0x7e, 0xb8, 0x65, 0xe0, 0x3b, 0xce, 0xe5, 0xcf, 0x90, 0x48, 0xa9, 0x39, - 0xd3, 0xd0, 0xfc, 0xd3, 0xc7, 0xb3, 0x7d, 0xf3, 0xc0, 0x09, 0xbf, 0xfc, 0xf6, 0x70, 0xfc, 0x4c, - 0xe5, 0x02, 0xa8, 0x9c, 0xc3, 0xa5, 0x3e, 0x95, 0x20, 0xf1, 0x27, 0x0d, 0x95, 0x06, 0x0d, 0xca, - 0xd5, 0x81, 0xc1, 0x06, 0x30, 0xcb, 0x6f, 0xde, 0x94, 0x99, 0x09, 0x5a, 0x01, 0x41, 0xcb, 0xd8, - 0xec, 0x13, 0x94, 0x7e, 0x24, 0xac, 0x75, 0x47, 0xa9, 0xfe, 0xad, 0x86, 0xa6, 0xaf, 0xcc, 0xc6, - 0xd7, 0x07, 0x86, 0xeb, 0xa7, 0x95, 0xd7, 0x6e, 0x44, 0xcb, 0x24, 0x2d, 0x81, 0xa4, 0x97, 0xf1, - 0x5c, 0xff, 0x49, 0x02, 0xad, 0xb2, 0xfd, 0xe8, 0xdc, 0xd4, 0x1e, 0x9f, 0x9b, 0xda, 0x9f, 0xe7, - 0xa6, 0xf6, 0xfd, 0x85, 0x39, 0xf2, 0xf8, 0xc2, 0x1c, 0xf9, 0xed, 0xc2, 0x1c, 0xf9, 0xe2, 0xde, - 0x75, 0x5d, 0x5c, 0x39, 0x82, 0x57, 0x55, 0x1f, 0x87, 0x56, 0xf2, 0xd6, 0xff, 0x01, 0x00, 0x00, - 0xff, 0xff, 0x18, 0x46, 0x67, 0xdd, 0x38, 0x0b, 0x00, 0x00, + // 1100 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x41, 0x6b, 0x24, 0x45, + 0x14, 0x4e, 0x6f, 0x62, 0xcc, 0xd4, 0x98, 0xdd, 0xa4, 0x27, 0x59, 0x3b, 0x93, 0xa4, 0x3b, 0x96, + 0x1a, 0xb3, 0x60, 0xba, 0x4d, 0x14, 0xd1, 0x08, 0xa2, 0xb3, 0xd9, 0xc8, 0x42, 0x46, 0x87, 0x42, + 0x56, 0xf0, 0x32, 0xd4, 0x4c, 0x57, 0x7a, 0x9a, 0x74, 0x77, 0x35, 0x5d, 0x95, 0x4c, 0x02, 0xe2, + 0x41, 0x04, 0x3d, 0x0a, 0x82, 0xc4, 0xdb, 0xfc, 0x00, 0xc1, 0xbf, 0xb1, 0xc7, 0x05, 0x2f, 0xe2, + 0x61, 0x94, 0xc4, 0xc3, 0xe2, 0x41, 0x61, 0xee, 0x82, 0x54, 0x75, 0x4d, 0x4f, 0x67, 0x32, 0xbb, + 0x9b, 0xc1, 0xd3, 0x4c, 0xbd, 0xef, 0xab, 0xf7, 0xbe, 0xf7, 0xaa, 0xea, 0xbd, 0x06, 0x4b, 0x91, + 0xdf, 0xf0, 0x93, 0x23, 0x87, 0x26, 0xb8, 0x19, 0x10, 0xe7, 0x78, 0xcb, 0xe1, 0x27, 0x76, 0x9c, + 0x50, 0x4e, 0xf5, 0xb9, 0x14, 0xb2, 0x53, 0xc8, 0x3e, 0xde, 0x2a, 0x2f, 0x78, 0xd4, 0xa3, 0x12, + 0x74, 0xc4, 0xbf, 0x94, 0x57, 0x5e, 0xf1, 0x28, 0xf5, 0x02, 0xe2, 0xe0, 0xd8, 0x77, 0x70, 0x14, + 0x51, 0x8e, 0xb9, 0x4f, 0x23, 0xa6, 0x50, 0x53, 0xa1, 0x72, 0xd5, 0x38, 0x3a, 0x70, 0xdc, 0xa3, + 0x44, 0x12, 0x14, 0xbe, 0x7a, 0x45, 0x80, 0x8a, 0x27, 0x61, 0xf8, 0xb3, 0x06, 0xac, 0x2a, 0xf3, + 0x3e, 0xf4, 0xbc, 0x84, 0x78, 0x98, 0x93, 0x7b, 0x27, 0xcd, 0x16, 0x8e, 0x3c, 0x82, 0x30, 0x27, + 0xb5, 0x84, 0x1c, 0x53, 0x4e, 0xf4, 0x97, 0xc1, 0x54, 0x0b, 0xb3, 0x96, 0xa1, 0xad, 0x69, 0x1b, + 0x85, 0xca, 0xad, 0x5e, 0xd7, 0x2a, 0x9e, 0xe2, 0x30, 0xd8, 0x81, 0xc2, 0x0a, 0x91, 0x04, 0xf5, + 0x3b, 0x60, 0xfa, 0x80, 0x10, 0x97, 0x24, 0xc6, 0x0d, 0x49, 0x9b, 0xef, 0x75, 0xad, 0xd9, 0x94, + 0x96, 0xda, 0x21, 0x52, 0x04, 0x7d, 0x1b, 0x14, 0x8e, 0x71, 0xe0, 0xbb, 0x98, 0xd3, 0xc4, 0x98, + 0x94, 0xec, 0x85, 0x5e, 0xd7, 0x9a, 0x4b, 0xd9, 0x19, 0x04, 0xd1, 0x80, 0xb6, 0x33, 0xf3, 0x6d, + 0xc7, 0x9a, 0x78, 0xdc, 0xb1, 0x26, 0xe0, 0x1d, 0xf0, 0xda, 0x33, 0x04, 0x23, 0xc2, 0x62, 0x1a, + 0x31, 0x02, 0xff, 0xd6, 0xc0, 0xca, 0x93, 0xb8, 0x0f, 0x54, 0x66, 0x0c, 0x07, 0xfc, 0x6a, 0x66, + 0xc2, 0x0a, 0x91, 0x04, 0xf5, 0x0f, 0xc0, 0x4d, 0xa2, 0x36, 0xd6, 0x13, 0xcc, 0x09, 0x53, 0x19, + 0x2e, 0xf5, 0xba, 0xd6, 0x62, 0x4a, 0xbf, 0x8c, 0x43, 0x34, 0x4b, 0x72, 0x91, 0x58, 0xae, 0x36, + 0x93, 0x63, 0xd5, 0x66, 0x6a, 0xdc, 0xda, 0xac, 0x83, 0x57, 0x9e, 0x96, 0x6f, 0x56, 0x98, 0xaf, + 0x35, 0x70, 0xbb, 0xca, 0xbc, 0x5d, 0x12, 0x48, 0xde, 0x1e, 0x21, 0xee, 0x5d, 0x01, 0x44, 0x5c, + 0x77, 0xc0, 0x0c, 0x8d, 0x49, 0x22, 0xe3, 0xa7, 0x65, 0x29, 0xf5, 0xba, 0xd6, 0xad, 0x34, 0x7e, + 0x1f, 0x81, 0x28, 0x23, 0x89, 0x0d, 0xae, 0xf2, 0xa3, 0x0a, 0x93, 0xdb, 0xd0, 0x47, 0x20, 0xca, + 0x48, 0x39, 0xb9, 0x6b, 0xc0, 0x1c, 0xad, 0x22, 0x13, 0x7a, 0xa6, 0x81, 0x52, 0x95, 0x79, 0xf7, + 0x5c, 0x9f, 0x7f, 0x22, 0xaf, 0x6d, 0x0d, 0x27, 0x38, 0x94, 0x15, 0x65, 0x24, 0x12, 0x15, 0xd5, + 0x86, 0x2b, 0x9a, 0xda, 0x21, 0x52, 0x04, 0x7d, 0x1f, 0x4c, 0xc7, 0x72, 0x93, 0x54, 0x57, 0xdc, + 0x7e, 0xc9, 0x1e, 0x7e, 0x77, 0x76, 0xde, 0x75, 0x95, 0x79, 0x79, 0x6f, 0xe9, 0x56, 0x88, 0x94, + 0x8f, 0x9c, 0xf8, 0x55, 0xb0, 0x3c, 0x42, 0x59, 0xa6, 0xfc, 0x9f, 0x19, 0x70, 0x6b, 0xc8, 0xaf, + 0xfe, 0x1e, 0x28, 0x8a, 0xfb, 0x59, 0x8f, 0x49, 0xe2, 0x53, 0x57, 0x4a, 0x9f, 0xaa, 0x94, 0x1f, + 0x76, 0x2d, 0xad, 0xd7, 0xb5, 0x74, 0x75, 0xc4, 0x03, 0x02, 0x44, 0x40, 0xac, 0x6a, 0x72, 0xa1, + 0x47, 0xe0, 0xa6, 0xc4, 0x78, 0x2b, 0x21, 0xac, 0x45, 0x03, 0x57, 0x55, 0xfb, 0x23, 0xb1, 0xff, + 0xb7, 0xae, 0xb5, 0xee, 0xf9, 0xbc, 0x75, 0xd4, 0xb0, 0x9b, 0x34, 0x74, 0x9a, 0x94, 0x85, 0x94, + 0xa9, 0x9f, 0x4d, 0xe6, 0x1e, 0x3a, 0xfc, 0x34, 0x26, 0xcc, 0xde, 0x25, 0xcd, 0xc1, 0xa5, 0xbd, + 0xec, 0x0d, 0xa2, 0x59, 0x61, 0xf8, 0xb4, 0xbf, 0xd6, 0x09, 0x28, 0x26, 0xa4, 0x8d, 0x13, 0xb7, + 0xde, 0xc0, 0x91, 0xab, 0x6e, 0xee, 0xee, 0xd8, 0xc1, 0x54, 0x5a, 0x39, 0x57, 0x10, 0x81, 0x74, + 0x55, 0xc1, 0x91, 0x48, 0xab, 0xd0, 0x6e, 0xf9, 0x9c, 0x04, 0x3e, 0xe3, 0xc6, 0xd4, 0xda, 0xe4, + 0x46, 0xa1, 0x52, 0x53, 0x41, 0xde, 0xca, 0x05, 0xf9, 0x58, 0x9e, 0xd9, 0xdd, 0x16, 0xf6, 0x23, + 0x47, 0x75, 0xb4, 0xe3, 0x6d, 0xe7, 0xc4, 0x69, 0xd2, 0x30, 0xa4, 0x91, 0x83, 0x19, 0x23, 0xdc, + 0xae, 0x61, 0x3f, 0x19, 0x3c, 0x96, 0xcc, 0x2d, 0x44, 0x83, 0x10, 0xa2, 0x8c, 0x2c, 0xc0, 0xac, + 0x55, 0x3f, 0x48, 0x70, 0x53, 0xf4, 0x49, 0xe3, 0xb9, 0xff, 0x57, 0xc6, 0xcb, 0xde, 0x20, 0x9a, + 0x95, 0x86, 0x3d, 0xb5, 0xd6, 0xdf, 0x07, 0x2f, 0xa4, 0x8c, 0xb6, 0x1f, 0xb9, 0xb4, 0x6d, 0x4c, + 0xcb, 0x43, 0x5f, 0x56, 0x87, 0x5e, 0xca, 0xfb, 0x48, 0x19, 0x10, 0x15, 0xe5, 0xf2, 0x33, 0xb9, + 0xd2, 0xbf, 0x04, 0x0b, 0xa1, 0x1f, 0xd5, 0xe5, 0x6b, 0x17, 0xf7, 0xa2, 0xef, 0xe7, 0x79, 0xa9, + 0xba, 0x3a, 0xb6, 0xea, 0xe5, 0x34, 0xe2, 0x28, 0x9f, 0x10, 0xcd, 0x87, 0x7e, 0xf4, 0x40, 0x58, + 0x6b, 0x24, 0x51, 0xf1, 0x7f, 0xd0, 0xc0, 0x02, 0x6f, 0xe3, 0xb8, 0x1e, 0x50, 0x7a, 0xd8, 0xc0, + 0xcd, 0xc3, 0xbe, 0x80, 0x19, 0xf9, 0x9a, 0x96, 0xec, 0x74, 0xfe, 0xd8, 0xfd, 0xf9, 0x63, 0xef, + 0xaa, 0xf9, 0x53, 0xb9, 0x2f, 0xb4, 0xfd, 0xd5, 0xb5, 0xcc, 0x51, 0xdb, 0x5f, 0xa7, 0xa1, 0xcf, + 0x49, 0x18, 0xf3, 0xd3, 0x81, 0xa6, 0x51, 0x3c, 0x78, 0xf6, 0xbb, 0xa5, 0x21, 0x5d, 0x40, 0xfb, + 0x0a, 0x51, 0xc2, 0xde, 0x01, 0x40, 0x26, 0x41, 0x39, 0x49, 0x98, 0x51, 0x90, 0x65, 0x5d, 0x52, + 0x65, 0x9d, 0xcf, 0x25, 0x29, 0x71, 0x88, 0x0a, 0x22, 0x35, 0xf9, 0x5f, 0xff, 0x02, 0x94, 0xb2, + 0xe6, 0x59, 0x3f, 0x20, 0xb2, 0x6b, 0xfb, 0xd4, 0x00, 0xb2, 0xa2, 0xfb, 0x63, 0x57, 0xb4, 0x3c, + 0xd4, 0x9b, 0x07, 0x2e, 0x21, 0x9a, 0xcf, 0xac, 0x7b, 0x44, 0xb4, 0x61, 0x9f, 0xea, 0x55, 0x30, + 0x4f, 0x4e, 0x62, 0x3f, 0x2d, 0x52, 0xbd, 0x11, 0xd0, 0xe6, 0x21, 0x33, 0x8a, 0x52, 0xfe, 0x9a, + 0x92, 0x6f, 0xf4, 0xa7, 0xca, 0x10, 0x0d, 0xa2, 0xb9, 0x81, 0xad, 0x22, 0x4d, 0x3b, 0x33, 0x67, + 0x1d, 0x4b, 0x7b, 0xdc, 0xb1, 0xb4, 0xed, 0x7f, 0xa7, 0xc0, 0xa4, 0xe8, 0x32, 0x3f, 0x69, 0x60, + 0xe5, 0xa9, 0xf3, 0x7c, 0xeb, 0x6a, 0x07, 0x7c, 0xc6, 0x44, 0x2d, 0xbf, 0x3b, 0xf6, 0x96, 0xac, + 0x11, 0x9a, 0x5f, 0xfd, 0xf2, 0xe7, 0xf7, 0x37, 0x0c, 0x78, 0xdb, 0xb9, 0xfc, 0x25, 0x12, 0x2b, + 0x35, 0x1d, 0x0d, 0x2c, 0x3d, 0x79, 0x42, 0xdb, 0xd7, 0x0f, 0x2c, 0xf8, 0xe5, 0xb7, 0xc7, 0xe3, + 0x67, 0x2a, 0x97, 0xa5, 0xca, 0x45, 0x58, 0x1a, 0x52, 0x29, 0x25, 0xfe, 0xa8, 0x81, 0xd2, 0xa8, + 0x59, 0xb9, 0x31, 0x32, 0xd8, 0x08, 0x66, 0xf9, 0x8d, 0xeb, 0x32, 0x33, 0x41, 0xeb, 0x52, 0xd0, + 0x1a, 0x34, 0x87, 0x04, 0xa5, 0xdf, 0x09, 0x9b, 0xfd, 0x69, 0xaa, 0x7f, 0xa3, 0x81, 0xb9, 0x2b, + 0xe3, 0xf1, 0xd5, 0x91, 0xe1, 0x86, 0x69, 0xe5, 0xcd, 0x6b, 0xd1, 0x32, 0x49, 0xab, 0x52, 0xd2, + 0x8b, 0x70, 0x71, 0xf8, 0x24, 0x25, 0xad, 0x72, 0xff, 0xe1, 0xb9, 0xa9, 0x3d, 0x3a, 0x37, 0xb5, + 0x3f, 0xce, 0x4d, 0xed, 0xbb, 0x0b, 0x73, 0xe2, 0xd1, 0x85, 0x39, 0xf1, 0xeb, 0x85, 0x39, 0xf1, + 0xb9, 0x73, 0x8d, 0x46, 0xae, 0x7c, 0xc9, 0x87, 0xd5, 0x98, 0x96, 0xdd, 0xe4, 0xcd, 0xff, 0x02, + 0x00, 0x00, 0xff, 0xff, 0x23, 0xfe, 0x97, 0x7a, 0x3e, 0x0b, 0x00, 0x00, } func (this *OracleParamsMsg) Equal(that interface{}) bool { @@ -2320,7 +2320,7 @@ func (m *OracleParamsMsg) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - var v github_com_NibiruChain_nibiru_x_common_asset.Pair + var v github_com_NibiruChain_nibiru_v2_x_common_asset.Pair m.Whitelist = append(m.Whitelist, v) if err := m.Whitelist[len(m.Whitelist)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err diff --git a/x/oracle/types/vote.go b/x/oracle/types/vote.go index a82e6d351..f95b0a53c 100644 --- a/x/oracle/types/vote.go +++ b/x/oracle/types/vote.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "github.com/NibiruChain/nibiru/x/common/asset" - "github.com/NibiruChain/nibiru/x/common/set" + "cosmossdk.io/math" - "gopkg.in/yaml.v2" + "github.com/NibiruChain/nibiru/v2/x/common/asset" + "github.com/NibiruChain/nibiru/v2/x/common/set" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -28,12 +28,6 @@ func NewAggregateExchangeRatePrevote(hash AggregateVoteHash, voter sdk.ValAddres } } -// String implement stringify -func (v AggregateExchangeRatePrevote) String() string { - out, _ := yaml.Marshal(v) - return string(out) -} - // NewAggregateExchangeRateVote creates a AggregateExchangeRateVote instance func NewAggregateExchangeRateVote(exchangeRateTuples ExchangeRateTuples, voter sdk.ValAddress) AggregateExchangeRateVote { return AggregateExchangeRateVote{ @@ -42,12 +36,6 @@ func NewAggregateExchangeRateVote(exchangeRateTuples ExchangeRateTuples, voter s } } -// String implement stringify -func (v AggregateExchangeRateVote) String() string { - out, _ := yaml.Marshal(v) - return string(out) -} - // NewExchangeRateTuple creates a ExchangeRateTuple instance func NewExchangeRateTuple(pair asset.Pair, exchangeRate sdk.Dec) ExchangeRateTuple { return ExchangeRateTuple{ @@ -56,12 +44,6 @@ func NewExchangeRateTuple(pair asset.Pair, exchangeRate sdk.Dec) ExchangeRateTup } } -// String implement stringify -func (m ExchangeRateTuple) String() string { - out, _ := yaml.Marshal(m) - return string(out) -} - // ToString converts the ExchangeRateTuple to the vote string. func (m ExchangeRateTuple) ToString() (string, error) { err := m.Pair.Validate() @@ -101,7 +83,7 @@ func NewExchangeRateTupleFromString(s string) (ExchangeRateTuple, error) { return ExchangeRateTuple{}, fmt.Errorf("invalid pair definition %s: %w", split[0], err) } - dec, err := sdk.NewDecFromStr(split[1]) + dec, err := math.LegacyNewDecFromStr(split[1]) if err != nil { return ExchangeRateTuple{}, fmt.Errorf("invalid decimal %s: %w", split[1], err) } @@ -123,12 +105,6 @@ func (tuples ExchangeRateTuples) ToMap() (exchangeRateMap map[asset.Pair]sdk.Dec return exchangeRateMap } -// String implements fmt.Stringer interface -func (tuples ExchangeRateTuples) String() string { - out, _ := yaml.Marshal(tuples) - return string(out) -} - func NewExchangeRateTuplesFromString(s string) (ExchangeRateTuples, error) { stringTuples := strings.Split(s, ExchangeRateTuplesSeparator) diff --git a/x/oracle/types/vote_test.go b/x/oracle/types/vote_test.go index 1480d750b..dce6f8cf4 100644 --- a/x/oracle/types/vote_test.go +++ b/x/oracle/types/vote_test.go @@ -3,11 +3,10 @@ package types_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - + "cosmossdk.io/math" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/oracle/types" ) func TestExchangeRateTuples_ToString(t *testing.T) { @@ -15,12 +14,12 @@ func TestExchangeRateTuples_ToString(t *testing.T) { tuples := types.ExchangeRateTuples{ { Pair: "BTC:USD", - ExchangeRate: sdk.MustNewDecFromStr("40000.00"), + ExchangeRate: math.LegacyMustNewDecFromStr("40000.00"), }, { Pair: "ETH:USD", - ExchangeRate: sdk.MustNewDecFromStr("4000.00"), + ExchangeRate: math.LegacyMustNewDecFromStr("4000.00"), }, } @@ -37,12 +36,12 @@ func TestExchangeRateTuples_ToString(t *testing.T) { tuples := types.ExchangeRateTuples{ { Pair: "BTC:USD", - ExchangeRate: sdk.MustNewDecFromStr("40000.00"), + ExchangeRate: math.LegacyMustNewDecFromStr("40000.00"), }, { Pair: "BTC:USD", - ExchangeRate: sdk.MustNewDecFromStr("4000.00"), + ExchangeRate: math.LegacyMustNewDecFromStr("4000.00"), }, } @@ -58,7 +57,7 @@ func TestExchangeRateTuple(t *testing.T) { t.Run("inverse", func(t *testing.T) { exchangeRate := types.ExchangeRateTuple{ Pair: "BTC:USD", - ExchangeRate: sdk.MustNewDecFromStr("40000.00"), + ExchangeRate: math.LegacyMustNewDecFromStr("40000.00"), } exchangeRateStr, err := exchangeRate.ToString() require.NoError(t, err) diff --git a/x/sudo/cli/cli.go b/x/sudo/cli/cli.go index 3e53138c7..a60fdac24 100644 --- a/x/sudo/cli/cli.go +++ b/x/sudo/cli/cli.go @@ -5,7 +5,7 @@ import ( "os" "strings" - "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" diff --git a/x/sudo/cli/cli_test.go b/x/sudo/cli/cli_test.go index c6c45a3bc..2dd167a18 100644 --- a/x/sudo/cli/cli_test.go +++ b/x/sudo/cli/cli_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo/types" "github.com/cosmos/gogoproto/jsonpb" "github.com/stretchr/testify/assert" @@ -18,15 +18,15 @@ import ( sdktestutil "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/set" - "github.com/NibiruChain/nibiru/x/common/testutil" - testutilcli "github.com/NibiruChain/nibiru/x/common/testutil/cli" - "github.com/NibiruChain/nibiru/x/common/testutil/genesis" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/sudo/cli" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" + "github.com/NibiruChain/nibiru/v2/x/sudo/cli" ) // ——————————————————————————————————————————————————————————————————— @@ -70,7 +70,7 @@ func (msg MsgEditSudoersPlus) ToJson(t *testing.T) (fileJsonBz []byte, fileName } func (MsgEditSudoersPlus) Exec( - network *testutilcli.Network, + network *testnetwork.Network, fileName string, from sdk.AccAddress, ) (*sdk.TxResponse, error) { @@ -80,10 +80,12 @@ func (MsgEditSudoersPlus) Exec( return network.ExecTxCmd(cli.CmdEditSudoers(), from, args) } -type IntegrationSuite struct { +var _ suite.TearDownAllSuite = (*TestSuite)(nil) + +type TestSuite struct { suite.Suite - cfg testutilcli.Config - network *testutilcli.Network + cfg testnetwork.Config + network *testnetwork.Network root Account } @@ -94,14 +96,16 @@ type Account struct { } func TestSuite_IntegrationSuite_RunAll(t *testing.T) { - suite.Run(t, new(IntegrationSuite)) + testutil.RetrySuiteRunIfDbClosed(t, func() { + suite.Run(t, new(TestSuite)) + }, 2) } // ——————————————————————————————————————————————————————————————————— // IntegrationSuite - Setup // ——————————————————————————————————————————————————————————————————— -func (s *IntegrationSuite) SetupSuite() { +func (s *TestSuite) SetupSuite() { testutil.BeforeIntegrationSuite(s.T()) testapp.EnsureNibiruPrefix() @@ -113,8 +117,8 @@ func (s *IntegrationSuite) SetupSuite() { passphrase: "secure-password", } homeDir := s.T().TempDir() - s.cfg = testutilcli.BuildNetworkConfig(genState) - network, err := testutilcli.New(s.T(), homeDir, s.cfg) + s.cfg = testnetwork.BuildNetworkConfig(genState) + network, err := testnetwork.New(s.T(), homeDir, s.cfg) s.Require().NoError(err) s.network = network @@ -122,18 +126,20 @@ func (s *IntegrationSuite) SetupSuite() { s.AddRootToKeyring(s.root) } -func (s *IntegrationSuite) FundRoot(root Account) { +func (s *TestSuite) FundRoot(root Account) { val := s.network.Validators[0] funds := sdk.NewCoins( sdk.NewInt64Coin(denoms.NIBI, 420*common.TO_MICRO), ) feeDenom := denoms.NIBI - s.NoError(testutilcli.FillWalletFromValidator( + + _, err := testnetwork.FillWalletFromValidator( root.addr, funds, val, feeDenom, - )) + ) + s.NoError(err) } -func (s *IntegrationSuite) AddRootToKeyring(root Account) { +func (s *TestSuite) AddRootToKeyring(root Account) { s.T().Log("add the x/sudo root account to the clientCtx.Keyring") // Encrypt the x/sudo root account's private key to get its "armor" passphrase := root.passphrase @@ -150,7 +156,7 @@ func (s *IntegrationSuite) AddRootToKeyring(root Account) { // IntegrationSuite - Tests // ——————————————————————————————————————————————————————————————————— -func (s *IntegrationSuite) TestCmdEditSudoers() { +func (s *TestSuite) TestCmdEditSudoers() { val := s.network.Validators[0] _, contractAddrs := testutil.PrivKeyAddressPairs(3) @@ -182,7 +188,7 @@ func (s *IntegrationSuite) TestCmdEditSudoers() { out, err = msg.Exec(s.network, fileName, sender) s.NoErrorf(err, "msg: %s\nout: %s", jsonBz, out) - state, err := testutilcli.QuerySudoers(val.ClientCtx) + state, err := testnetwork.QuerySudoers(val.ClientCtx) s.NoError(err) gotRoot := state.Sudoers.Root @@ -207,7 +213,7 @@ func (s *IntegrationSuite) TestCmdEditSudoers() { out, err = msg.Exec(s.network, fileName, sender) s.NoErrorf(err, "msg: %s\nout: %s", jsonBz, out) - state, err = testutilcli.QuerySudoers(val.ClientCtx) + state, err = testnetwork.QuerySudoers(val.ClientCtx) s.NoError(err) gotRoot = state.Sudoers.Root @@ -221,10 +227,10 @@ func (s *IntegrationSuite) TestCmdEditSudoers() { } } -func (s *IntegrationSuite) Test_ZCmdChangeRoot() { +func (s *TestSuite) Test_ZCmdChangeRoot() { val := s.network.Validators[0] - sudoers, err := testutilcli.QuerySudoers(val.ClientCtx) + sudoers, err := testnetwork.QuerySudoers(val.ClientCtx) s.NoError(err) initialRoot := sudoers.Sudoers.Root @@ -233,7 +239,7 @@ func (s *IntegrationSuite) Test_ZCmdChangeRoot() { cli.CmdChangeRoot(), s.root.addr, []string{newRoot.String()}) require.NoError(s.T(), err) - sudoers, err = testutilcli.QuerySudoers(val.ClientCtx) + sudoers, err = testnetwork.QuerySudoers(val.ClientCtx) s.NoError(err) require.NotEqual(s.T(), sudoers.Sudoers.Root, initialRoot) require.Equal(s.T(), sudoers.Sudoers.Root, newRoot.String()) @@ -242,7 +248,7 @@ func (s *IntegrationSuite) Test_ZCmdChangeRoot() { // TestMarshal_EditSudoers verifies that the expected proto.Message for // the EditSudoders fn marshals and unmarshals properly from JSON. // This unmarshaling is used in the main body of the CmdEditSudoers command. -func (s *IntegrationSuite) TestMarshal_EditSudoers() { +func (s *TestSuite) TestMarshal_EditSudoers() { t := s.T() t.Log("create valid example json for the message") @@ -263,14 +269,14 @@ func (s *IntegrationSuite) TestMarshal_EditSudoers() { fileJsonBz, _ := msgPlus.ToJson(t) t.Log("check unmarshal file → proto") - cdc := app.MakeEncodingConfig().Marshaler + cdc := app.MakeEncodingConfig().Codec newMsg := new(types.MsgEditSudoers) err := cdc.UnmarshalJSON(fileJsonBz, newMsg) assert.NoErrorf(t, err, "fileJsonBz: #%v", fileJsonBz) require.NoError(t, newMsg.ValidateBasic(), newMsg.String()) } -func (s *IntegrationSuite) TearDownSuite() { +func (s *TestSuite) TearDownSuite() { s.T().Log("tearing down integration test suite") s.network.Cleanup() } diff --git a/x/sudo/cli/gen_root.go b/x/sudo/cli/gen_root.go index 4369ab3b8..c55d047f3 100644 --- a/x/sudo/cli/gen_root.go +++ b/x/sudo/cli/gen_root.go @@ -12,7 +12,7 @@ import ( genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) func AddSudoRootAccountCmd(defaultNodeHome string) *cobra.Command { diff --git a/x/sudo/cli/gen_root_test.go b/x/sudo/cli/gen_root_test.go index ef21536ca..ccb958fbb 100644 --- a/x/sudo/cli/gen_root_test.go +++ b/x/sudo/cli/gen_root_test.go @@ -5,8 +5,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/sudo/cli" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/sudo/cli" ) func TestAddSudoRootAccountCmd(t *testing.T) { @@ -26,6 +27,7 @@ func TestAddSudoRootAccountCmd(t *testing.T) { for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { + testapp.EnsureNibiruPrefix() ctx := testutil.SetupClientCtx(t) cmd := cli.AddSudoRootAccountCmd(t.TempDir()) cmd.SetArgs([]string{ diff --git a/x/sudo/doc.go b/x/sudo/doc.go index abff474ca..d75e435bd 100644 --- a/x/sudo/doc.go +++ b/x/sudo/doc.go @@ -1,7 +1,6 @@ package sudo /* - Package sudo provides a simple way to manage, verify, and run smart contracts with elevated permissions, mimicking the functionality of a Unix-based sudoers file. @@ -19,5 +18,4 @@ files. Note that this package does not provide actual system integration or execute commands with elevated privileges. It only offers a way to manage and verify permissions in a sudoers-like manner within your application. - */ diff --git a/x/sudo/genesis.go b/x/sudo/genesis.go index 9feb94e8d..b3e73e378 100644 --- a/x/sudo/genesis.go +++ b/x/sudo/genesis.go @@ -3,8 +3,8 @@ package sudo import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/sudo/keeper" - "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" + "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) // InitGenesis initializes the module's state from a provided genesis state JSON. diff --git a/x/sudo/keeper/keeper.go b/x/sudo/keeper/keeper.go index 7f2505d43..d1999ee05 100644 --- a/x/sudo/keeper/keeper.go +++ b/x/sudo/keeper/keeper.go @@ -9,8 +9,8 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/set" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/common/set" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) type Keeper struct { @@ -138,11 +138,11 @@ func (k Keeper) CheckPermissions( } contracts := state.Contracts - hasPermission := set.New(contracts...).Has(contract.String()) + hasPermission := set.New(contracts...).Has(contract.String()) || contract.String() == state.Root if !hasPermission { return fmt.Errorf( - "insufficient permissions on smart contract: %s. The sudo contracts are: %s", - contract, contracts, + "%w: insufficient permissions on smart contract: %s. The sudo contracts are: %s", + sudotypes.ErrUnauthorized, contract, contracts, ) } return nil diff --git a/x/sudo/keeper/keeper_test.go b/x/sudo/keeper/keeper_test.go index 9b9bfe2ad..6f1f7fbb1 100644 --- a/x/sudo/keeper/keeper_test.go +++ b/x/sudo/keeper/keeper_test.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) func TestCheckPermissions(t *testing.T) { diff --git a/x/sudo/keeper/msg_server.go b/x/sudo/keeper/msg_server.go index ad22c3afb..e44067bfd 100644 --- a/x/sudo/keeper/msg_server.go +++ b/x/sudo/keeper/msg_server.go @@ -9,8 +9,8 @@ import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/set" - sudotypes "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/common/set" + sudotypes "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) type MsgServer struct { @@ -89,7 +89,8 @@ type Sudoers struct { } func (sudo Sudoers) String() string { - return sudo.ToPb().String() + r := sudo.ToPb() + return r.String() } func (sudo Sudoers) ToPb() sudotypes.Sudoers { diff --git a/x/sudo/keeper/msg_server_test.go b/x/sudo/keeper/msg_server_test.go index 5c2d24d31..9244bd16b 100644 --- a/x/sudo/keeper/msg_server_test.go +++ b/x/sudo/keeper/msg_server_test.go @@ -4,19 +4,19 @@ import ( "testing" "time" - "github.com/NibiruChain/nibiru/x/sudo/keeper" + "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" - "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/set" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/sudo" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/sudo" ) func init() { diff --git a/x/sudo/keeper/querier.go b/x/sudo/keeper/querier.go index a4d224783..99f060939 100644 --- a/x/sudo/keeper/querier.go +++ b/x/sudo/keeper/querier.go @@ -3,7 +3,7 @@ package keeper import ( "context" - "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo/types" sdk "github.com/cosmos/cosmos-sdk/types" "google.golang.org/grpc/codes" diff --git a/x/sudo/keeper/querier_test.go b/x/sudo/keeper/querier_test.go index 4bfff282b..c90720704 100644 --- a/x/sudo/keeper/querier_test.go +++ b/x/sudo/keeper/querier_test.go @@ -6,8 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/sudo/keeper" - "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" + "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) func TestQuerySudoers(t *testing.T) { diff --git a/x/sudo/module.go b/x/sudo/module.go index cca6fc0bb..27131c732 100644 --- a/x/sudo/module.go +++ b/x/sudo/module.go @@ -11,18 +11,21 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/sudo/cli" - sudokeeper "github.com/NibiruChain/nibiru/x/sudo/keeper" - "github.com/NibiruChain/nibiru/x/sudo/types" + "github.com/NibiruChain/nibiru/v2/x/sudo/cli" + sudokeeper "github.com/NibiruChain/nibiru/v2/x/sudo/keeper" + simulation "github.com/NibiruChain/nibiru/v2/x/sudo/simulation" + "github.com/NibiruChain/nibiru/v2/x/sudo/types" ) // Ensure the interface is properly implemented at compile time var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} ) // ---------------------------------------------------------------------------- @@ -41,7 +44,6 @@ func (AppModuleBasic) Name() string { return types.ModuleName } -// RegisterInterfaces registers interfaces and implementations of the perp module. func (AppModuleBasic) RegisterInterfaces(interfaceRegistry codectypes.InterfaceRegistry) { types.RegisterInterfaces(interfaceRegistry) } @@ -151,3 +153,21 @@ func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +//---------------------------------------------------------------------------- +// AppModuleSimulation functions +//---------------------------------------------------------------------------- + +// GenerateGenesisState implements module.AppModuleSimulation. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RegisterStoreDecoder implements module.AppModuleSimulation. +func (AppModule) RegisterStoreDecoder(sdk.StoreDecoderRegistry) { +} + +// WeightedOperations implements module.AppModuleSimulation. +func (AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return nil +} diff --git a/x/sudo/simulation/genesis.go b/x/sudo/simulation/genesis.go new file mode 100644 index 000000000..004f696c2 --- /dev/null +++ b/x/sudo/simulation/genesis.go @@ -0,0 +1,30 @@ +package simulation + +// DONTCOVER + +import ( + "encoding/json" + "fmt" + + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/NibiruChain/nibiru/v2/x/sudo/types" +) + +func RandomizedGenState(simState *module.SimulationState) { + rootAddress := simState.Accounts[simState.Rand.Intn(len(simState.Accounts))].Address + + genState := types.GenesisState{ + Sudoers: types.Sudoers{ + Root: rootAddress.String(), + Contracts: []string{}, + }, + } + + bz, err := json.MarshalIndent(&genState, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated x/sudo parameters:\n%s\n", bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&genState) +} diff --git a/x/sudo/types/actions.go b/x/sudo/types/actions.go index 9d838bb8f..2b172727a 100644 --- a/x/sudo/types/actions.go +++ b/x/sudo/types/actions.go @@ -1,6 +1,6 @@ package types -import "github.com/NibiruChain/nibiru/x/common/set" +import "github.com/NibiruChain/nibiru/v2/x/common/set" type RootAction string diff --git a/x/sudo/types/event.pb.go b/x/sudo/types/event.pb.go index 8dc8afb33..b49d54d1e 100644 --- a/x/sudo/types/event.pb.go +++ b/x/sudo/types/event.pb.go @@ -24,6 +24,7 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// EventUpdateSudoers: ABCI event emitted upon execution of "MsgEditSudoers". type EventUpdateSudoers struct { Sudoers Sudoers `protobuf:"bytes,1,opt,name=sudoers,proto3" json:"sudoers"` // Action is the type of update that occured to the "sudoers" @@ -84,7 +85,7 @@ func init() { func init() { proto.RegisterFile("nibiru/sudo/v1/event.proto", fileDescriptor_7e6085948b018986) } var fileDescriptor_7e6085948b018986 = []byte{ - // 242 bytes of a gzipped FileDescriptorProto + // 245 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0xcb, 0x4c, 0xca, 0x2c, 0x2a, 0xd5, 0x2f, 0x2e, 0x4d, 0xc9, 0xd7, 0x2f, 0x33, 0xd4, 0x4f, 0x2d, 0x4b, 0xcd, 0x2b, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x83, 0xc8, 0xe9, 0x81, 0xe4, 0xf4, 0xca, 0x0c, @@ -95,12 +96,12 @@ var fileDescriptor_7e6085948b018986 = []byte{ 0xfc, 0xd4, 0xa2, 0x62, 0x21, 0x73, 0x2e, 0xf6, 0x62, 0x08, 0x53, 0x82, 0x51, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x5c, 0x0f, 0xd5, 0x1d, 0x7a, 0x50, 0x95, 0x4e, 0x2c, 0x27, 0xee, 0xc9, 0x33, 0x04, 0xc1, 0x54, 0x0b, 0x89, 0x71, 0xb1, 0x25, 0x26, 0x83, 0xec, 0x96, 0x60, 0x52, 0x60, 0xd4, 0xe0, - 0x0c, 0x82, 0xf2, 0x9c, 0x5c, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, + 0x0c, 0x82, 0xf2, 0x9c, 0xdc, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, - 0x2b, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0xdf, 0x0f, 0x6c, 0x87, 0x73, - 0x46, 0x62, 0x66, 0x9e, 0x3e, 0xd4, 0xcd, 0x15, 0x10, 0x57, 0x97, 0x54, 0x16, 0xa4, 0x16, 0x27, - 0xb1, 0x81, 0xdd, 0x6c, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xb3, 0x68, 0xee, 0x31, 0x01, - 0x00, 0x00, + 0x37, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0xdf, 0x0f, 0x6c, 0x87, 0x73, + 0x46, 0x62, 0x66, 0x9e, 0x3e, 0xd4, 0xcd, 0x65, 0x46, 0xfa, 0x15, 0x10, 0x87, 0x97, 0x54, 0x16, + 0xa4, 0x16, 0x27, 0xb1, 0x81, 0x9d, 0x6d, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x0d, 0xe3, 0x06, + 0x82, 0x34, 0x01, 0x00, 0x00, } func (m *EventUpdateSudoers) Marshal() (dAtA []byte, err error) { diff --git a/x/sudo/types/query.pb.go b/x/sudo/types/query.pb.go index af9c1399e..c226a26ee 100644 --- a/x/sudo/types/query.pb.go +++ b/x/sudo/types/query.pb.go @@ -118,7 +118,7 @@ func init() { func init() { proto.RegisterFile("nibiru/sudo/v1/query.proto", fileDescriptor_3c5c8e03d8d77d77) } var fileDescriptor_3c5c8e03d8d77d77 = []byte{ - // 280 bytes of a gzipped FileDescriptorProto + // 283 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0xcb, 0x4c, 0xca, 0x2c, 0x2a, 0xd5, 0x2f, 0x2e, 0x4d, 0xc9, 0xd7, 0x2f, 0x33, 0xd4, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x83, 0xc8, 0xe9, 0x81, 0xe4, 0xf4, 0xca, 0x0c, @@ -131,12 +131,12 @@ var fileDescriptor_3c5c8e03d8d77d77 = []byte{ 0x1e, 0xaa, 0x03, 0xf5, 0xa0, 0x3a, 0x9c, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0xa9, 0x36, 0x6a, 0x60, 0xe4, 0x62, 0x05, 0x9b, 0x28, 0x54, 0xce, 0xc5, 0x83, 0x6c, 0xb4, 0x90, 0x32, 0xba, 0x09, 0x58, 0xdc, 0x23, 0xa5, 0x82, 0x5f, 0x11, 0xc4, 0x75, 0x4a, 0x32, 0x4d, 0x97, 0x9f, 0x4c, - 0x66, 0x12, 0x13, 0x12, 0xd1, 0x47, 0xf6, 0x31, 0xd4, 0x09, 0x4e, 0x2e, 0x27, 0x1e, 0xc9, 0x31, + 0x66, 0x12, 0x13, 0x12, 0xd1, 0x47, 0xf6, 0x31, 0xd4, 0x09, 0x4e, 0xee, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, - 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x95, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, - 0x9f, 0xab, 0xef, 0x07, 0xd6, 0xe9, 0x9c, 0x91, 0x98, 0x99, 0x07, 0x33, 0xa5, 0x02, 0x62, 0x4e, - 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0xdc, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, - 0x97, 0xc1, 0x8d, 0xe0, 0xb5, 0x01, 0x00, 0x00, + 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x9b, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, + 0x9f, 0xab, 0xef, 0x07, 0xd6, 0xe9, 0x9c, 0x91, 0x98, 0x99, 0x07, 0x33, 0xa5, 0xcc, 0x48, 0xbf, + 0x02, 0x62, 0x54, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0xe8, 0x8c, 0x01, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x54, 0x39, 0xc9, 0x2a, 0xb8, 0x01, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/sudo/types/state.go b/x/sudo/types/state.go index 94128577e..80517bc78 100644 --- a/x/sudo/types/state.go +++ b/x/sudo/types/state.go @@ -1,8 +1,6 @@ package types import ( - "encoding/json" - sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -22,8 +20,3 @@ type SudoersJson struct { Root string `json:"root"` Contracts []string `json:"contracts"` } - -func (sudo Sudoers) String() string { - jsonBz, _ := json.Marshal(SudoersJson(sudo)) - return string(jsonBz) -} diff --git a/x/sudo/types/state.pb.go b/x/sudo/types/state.pb.go index 99b71715a..41192164b 100644 --- a/x/sudo/types/state.pb.go +++ b/x/sudo/types/state.pb.go @@ -31,8 +31,9 @@ type Sudoers struct { Contracts []string `protobuf:"bytes,2,rep,name=contracts,proto3" json:"contracts,omitempty"` } -func (m *Sudoers) Reset() { *m = Sudoers{} } -func (*Sudoers) ProtoMessage() {} +func (m *Sudoers) Reset() { *m = Sudoers{} } +func (m *Sudoers) String() string { return proto.CompactTextString(m) } +func (*Sudoers) ProtoMessage() {} func (*Sudoers) Descriptor() ([]byte, []int) { return fileDescriptor_4b462ff6aaf658cf, []int{0} } @@ -77,7 +78,7 @@ func (m *Sudoers) GetContracts() []string { return nil } -// GenesisState defines the module's genesis state. +// GenesisState: State for migrations and genesis for the x/sudo module. type GenesisState struct { Sudoers Sudoers `protobuf:"bytes,1,opt,name=sudoers,proto3" json:"sudoers"` } @@ -130,24 +131,24 @@ func init() { func init() { proto.RegisterFile("nibiru/sudo/v1/state.proto", fileDescriptor_4b462ff6aaf658cf) } var fileDescriptor_4b462ff6aaf658cf = []byte{ - // 264 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0xcb, 0x4c, 0xca, - 0x2c, 0x2a, 0xd5, 0x2f, 0x2e, 0x4d, 0xc9, 0xd7, 0x2f, 0x33, 0xd4, 0x2f, 0x2e, 0x49, 0x2c, 0x49, - 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x83, 0xc8, 0xe9, 0x81, 0xe4, 0xf4, 0xca, 0x0c, - 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x52, 0xfa, 0x20, 0x16, 0x44, 0x95, 0x94, 0x4c, 0x7a, - 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x7e, 0x62, 0x41, 0xa6, 0x7e, 0x62, 0x5e, 0x5e, 0x7e, 0x49, 0x62, - 0x49, 0x66, 0x7e, 0x5e, 0x31, 0x44, 0x56, 0xc9, 0x91, 0x8b, 0x3d, 0xb8, 0x34, 0x25, 0x3f, 0xb5, - 0xa8, 0x58, 0x48, 0x88, 0x8b, 0xa5, 0x28, 0x3f, 0xbf, 0x44, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, - 0x08, 0xcc, 0x16, 0x92, 0xe1, 0xe2, 0x4c, 0xce, 0xcf, 0x2b, 0x29, 0x4a, 0x4c, 0x2e, 0x29, 0x96, - 0x60, 0x52, 0x60, 0xd6, 0xe0, 0x0c, 0x42, 0x08, 0x58, 0xb1, 0xcc, 0x58, 0x20, 0xcf, 0xa0, 0xe4, - 0xce, 0xc5, 0xe3, 0x9e, 0x9a, 0x97, 0x5a, 0x9c, 0x59, 0x1c, 0x0c, 0x72, 0x9c, 0x90, 0x39, 0x17, - 0x7b, 0x31, 0xc4, 0x48, 0xb0, 0x51, 0xdc, 0x46, 0xe2, 0x7a, 0xa8, 0x0e, 0xd5, 0x83, 0xda, 0xe8, - 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x4c, 0xb5, 0x93, 0xcb, 0x89, 0x47, 0x72, 0x8c, 0x17, - 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, - 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x69, 0xa5, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, - 0xea, 0xfb, 0x81, 0xcd, 0x72, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x87, 0x06, 0x4e, 0x05, 0x24, 0x78, - 0x4a, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x1e, 0x33, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, - 0x5e, 0x2c, 0x41, 0x6d, 0x3a, 0x01, 0x00, 0x00, + // 259 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xb1, 0x4e, 0xc3, 0x30, + 0x18, 0x84, 0x13, 0xa8, 0xa8, 0x62, 0x10, 0x43, 0x84, 0x44, 0x15, 0x55, 0xa6, 0xea, 0xd4, 0x05, + 0x5b, 0x2d, 0x03, 0x03, 0x5b, 0x19, 0xb2, 0x31, 0xa4, 0x1b, 0x9b, 0x93, 0x5a, 0xae, 0x25, 0xf0, + 0x1f, 0xd9, 0x7f, 0x22, 0x78, 0x0b, 0x1e, 0xab, 0x63, 0x47, 0x26, 0x84, 0x92, 0x17, 0x41, 0xb1, + 0x8b, 0x10, 0xdb, 0xc9, 0xdf, 0xe9, 0x7c, 0xff, 0x91, 0xcc, 0xe8, 0x52, 0xdb, 0x86, 0xbb, 0x66, + 0x0b, 0xbc, 0x5d, 0x72, 0x87, 0x02, 0x25, 0xab, 0x2d, 0x20, 0xa4, 0x97, 0x81, 0xb1, 0x81, 0xb1, + 0x76, 0x99, 0x5d, 0x29, 0x50, 0xe0, 0x11, 0x1f, 0x54, 0x70, 0x65, 0x53, 0x05, 0xa0, 0x5e, 0x24, + 0x17, 0xb5, 0xe6, 0xc2, 0x18, 0x40, 0x81, 0x1a, 0x8c, 0x0b, 0x74, 0xfe, 0x40, 0xc6, 0x9b, 0x66, + 0x0b, 0xd2, 0xba, 0x34, 0x25, 0x23, 0x0b, 0x80, 0x93, 0x78, 0x16, 0x2f, 0x92, 0xc2, 0xeb, 0x74, + 0x4a, 0x92, 0x0a, 0x0c, 0x5a, 0x51, 0xa1, 0x9b, 0x9c, 0xcc, 0x4e, 0x17, 0x49, 0xf1, 0xf7, 0x30, + 0xcf, 0xc9, 0x45, 0x2e, 0x8d, 0x74, 0xda, 0x6d, 0x86, 0x5a, 0xe9, 0x3d, 0x19, 0xbb, 0x10, 0xe6, + 0x43, 0xce, 0x57, 0xd7, 0xec, 0x7f, 0x45, 0x76, 0xfc, 0x6b, 0x3d, 0xda, 0x7f, 0xdd, 0x44, 0xc5, + 0xaf, 0x7b, 0x9d, 0xef, 0x3b, 0x1a, 0x1f, 0x3a, 0x1a, 0x7f, 0x77, 0x34, 0xfe, 0xe8, 0x69, 0x74, + 0xe8, 0x69, 0xf4, 0xd9, 0xd3, 0xe8, 0xf9, 0x56, 0x69, 0xdc, 0x35, 0x25, 0xab, 0xe0, 0x95, 0x3f, + 0xf9, 0xac, 0xc7, 0x9d, 0xd0, 0x86, 0x1f, 0x67, 0x69, 0x57, 0xfc, 0x2d, 0x6c, 0x83, 0xef, 0xb5, + 0x74, 0xe5, 0x99, 0xbf, 0xea, 0xee, 0x27, 0x00, 0x00, 0xff, 0xff, 0x84, 0x4f, 0x6d, 0x13, 0x37, + 0x01, 0x00, 0x00, } func (m *Sudoers) Marshal() (dAtA []byte, err error) { diff --git a/x/sudo/types/tx.pb.go b/x/sudo/types/tx.pb.go index 6d59db760..69509147c 100644 --- a/x/sudo/types/tx.pb.go +++ b/x/sudo/types/tx.pb.go @@ -234,30 +234,31 @@ func init() { func init() { proto.RegisterFile("nibiru/sudo/v1/tx.proto", fileDescriptor_a610e3c1609cdcbc) } var fileDescriptor_a610e3c1609cdcbc = []byte{ - // 368 bytes of a gzipped FileDescriptorProto + // 372 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xcd, 0x4e, 0xea, 0x40, - 0x18, 0x86, 0x29, 0x24, 0x9c, 0xc3, 0x9c, 0x1c, 0x16, 0x8d, 0x42, 0xa9, 0xd8, 0x60, 0x13, 0x0d, - 0x71, 0xd1, 0x09, 0x7a, 0x07, 0xa0, 0x4b, 0x5c, 0xd4, 0x9d, 0x0b, 0xc9, 0xd0, 0x4e, 0x86, 0x49, - 0x74, 0xbe, 0xa6, 0x33, 0x05, 0xdc, 0x7a, 0x05, 0x26, 0xde, 0x94, 0x4b, 0x12, 0x37, 0x2e, 0x0d, - 0x78, 0x0b, 0xee, 0x4d, 0xa7, 0xfc, 0xb4, 0x89, 0x61, 0xd7, 0xe9, 0xf3, 0xbd, 0xcf, 0x3b, 0x33, - 0x2d, 0x6a, 0x0a, 0x3e, 0xe6, 0x71, 0x82, 0x65, 0x12, 0x02, 0x9e, 0xf6, 0xb0, 0x9a, 0x7b, 0x51, - 0x0c, 0x0a, 0xcc, 0x7a, 0x06, 0xbc, 0x14, 0x78, 0xd3, 0x9e, 0x7d, 0xc0, 0x80, 0x81, 0x46, 0x38, - 0x7d, 0xca, 0xa6, 0xec, 0x36, 0x03, 0x60, 0x0f, 0x14, 0x93, 0x88, 0x63, 0x22, 0x04, 0x28, 0xa2, - 0x38, 0x08, 0x99, 0x51, 0xf7, 0x1e, 0xd5, 0x87, 0x92, 0x5d, 0x87, 0x5c, 0xdd, 0x26, 0x21, 0xd0, - 0x58, 0x9a, 0x0d, 0x54, 0x25, 0x41, 0x3a, 0x62, 0x19, 0x1d, 0xa3, 0x5b, 0xf3, 0xd7, 0x2b, 0xb3, - 0x8d, 0x6a, 0x01, 0x08, 0x15, 0x93, 0x40, 0x49, 0xab, 0xdc, 0xa9, 0x74, 0x6b, 0xfe, 0xee, 0x45, - 0x9a, 0x92, 0x54, 0x84, 0x34, 0xb6, 0x2a, 0x59, 0x2a, 0x5b, 0xb9, 0x16, 0x6a, 0x14, 0xfd, 0x3e, - 0x95, 0x11, 0x08, 0x49, 0xdd, 0x3e, 0xfa, 0x3f, 0x94, 0x6c, 0x30, 0x21, 0x82, 0x51, 0x1f, 0x40, - 0xe5, 0x14, 0x46, 0x5e, 0x61, 0xb6, 0xd0, 0x5f, 0x41, 0x67, 0xa3, 0x18, 0x40, 0x59, 0x65, 0x4d, - 0xfe, 0x08, 0x3a, 0x4b, 0x23, 0x6e, 0x13, 0x1d, 0x16, 0x1c, 0x1b, 0xf9, 0xc5, 0xb7, 0x81, 0x2a, - 0x43, 0xc9, 0xcc, 0x39, 0xfa, 0x97, 0x3f, 0x9b, 0xe3, 0x15, 0xaf, 0xcc, 0x2b, 0xee, 0xcd, 0x3e, - 0xdb, 0xcf, 0xb7, 0x7b, 0x3f, 0x79, 0x7e, 0xff, 0x7a, 0x2d, 0x1f, 0xb9, 0x2d, 0x9c, 0xff, 0x36, - 0x34, 0xe4, 0x6a, 0x24, 0xd7, 0x55, 0x0a, 0xa1, 0xdc, 0xd9, 0x8e, 0x7f, 0x11, 0xef, 0xb0, 0x7d, - 0xba, 0x17, 0x6f, 0x6b, 0x3b, 0xba, 0xd6, 0x76, 0xad, 0x42, 0x6d, 0xa0, 0x07, 0xf5, 0xfd, 0xf4, - 0xaf, 0xde, 0x96, 0x8e, 0xb1, 0x58, 0x3a, 0xc6, 0xe7, 0xd2, 0x31, 0x5e, 0x56, 0x4e, 0x69, 0xb1, - 0x72, 0x4a, 0x1f, 0x2b, 0xa7, 0x74, 0x77, 0xce, 0xb8, 0x9a, 0x24, 0x63, 0x2f, 0x80, 0x47, 0x7c, - 0xa3, 0xd3, 0x83, 0x09, 0xe1, 0x62, 0x63, 0x9a, 0x67, 0x2e, 0xf5, 0x14, 0x51, 0x39, 0xae, 0xea, - 0x7f, 0xe3, 0xf2, 0x27, 0x00, 0x00, 0xff, 0xff, 0x0d, 0x5c, 0x76, 0xe7, 0x7a, 0x02, 0x00, 0x00, + 0x18, 0x86, 0x29, 0x24, 0x9c, 0xc3, 0x9c, 0x1c, 0x16, 0xcd, 0x39, 0x50, 0x2a, 0x36, 0xd8, 0x44, + 0xc3, 0xc6, 0x4e, 0xc0, 0x3b, 0x80, 0x18, 0x57, 0xb8, 0xa8, 0x3b, 0x17, 0x92, 0xa1, 0x9d, 0x0c, + 0x93, 0xe8, 0x7c, 0x4d, 0x67, 0x0a, 0xb8, 0xf5, 0x0a, 0x4c, 0xbc, 0x29, 0x97, 0x24, 0x6e, 0x5c, + 0x1a, 0xf0, 0x16, 0xdc, 0x9b, 0x4e, 0xf9, 0x69, 0x13, 0xc3, 0xae, 0xd3, 0xe7, 0x7b, 0x9f, 0x77, + 0x66, 0x5a, 0xd4, 0x14, 0x7c, 0xc2, 0xe3, 0x04, 0xcb, 0x24, 0x04, 0x3c, 0xeb, 0x61, 0xb5, 0xf0, + 0xa2, 0x18, 0x14, 0x98, 0xf5, 0x0c, 0x78, 0x29, 0xf0, 0x66, 0x3d, 0xfb, 0x1f, 0x03, 0x06, 0x1a, + 0xe1, 0xf4, 0x29, 0x9b, 0xb2, 0xdb, 0x0c, 0x80, 0xdd, 0x53, 0x4c, 0x22, 0x8e, 0x89, 0x10, 0xa0, + 0x88, 0xe2, 0x20, 0x64, 0x46, 0xdd, 0x3b, 0x54, 0x1f, 0x49, 0x76, 0x19, 0x72, 0x75, 0x93, 0x84, + 0x40, 0x63, 0x69, 0x36, 0x50, 0x95, 0x04, 0xe9, 0x88, 0x65, 0x74, 0x8c, 0x6e, 0xcd, 0xdf, 0xac, + 0xcc, 0x36, 0xaa, 0x05, 0x20, 0x54, 0x4c, 0x02, 0x25, 0xad, 0x72, 0xa7, 0xd2, 0xad, 0xf9, 0xfb, + 0x17, 0x69, 0x4a, 0x52, 0x11, 0xd2, 0xd8, 0xaa, 0x64, 0xa9, 0x6c, 0xe5, 0x5a, 0xa8, 0x51, 0xf4, + 0xfb, 0x54, 0x46, 0x20, 0x24, 0x75, 0x07, 0xe8, 0xef, 0x48, 0xb2, 0xe1, 0x94, 0x08, 0x46, 0x7d, + 0x00, 0x95, 0x53, 0x18, 0x79, 0x85, 0xd9, 0x42, 0xbf, 0x05, 0x9d, 0x8f, 0x63, 0x00, 0x65, 0x95, + 0x35, 0xf9, 0x25, 0xe8, 0x3c, 0x8d, 0xb8, 0x4d, 0xf4, 0xbf, 0xe0, 0xd8, 0xca, 0xfb, 0x5f, 0x06, + 0xaa, 0x8c, 0x24, 0x33, 0x17, 0xe8, 0x4f, 0xfe, 0x6c, 0x8e, 0x57, 0xbc, 0x32, 0xaf, 0xb8, 0x37, + 0xfb, 0xec, 0x30, 0xdf, 0xed, 0xfd, 0xe4, 0xe9, 0xed, 0xf3, 0xa5, 0x7c, 0xe4, 0xb6, 0x70, 0xfe, + 0xdb, 0xd0, 0x90, 0xab, 0xb1, 0xdc, 0x54, 0x29, 0x84, 0x72, 0x67, 0x3b, 0xfe, 0x41, 0xbc, 0xc7, + 0xf6, 0xe9, 0x41, 0xbc, 0xab, 0xed, 0xe8, 0x5a, 0xdb, 0xb5, 0x0a, 0xb5, 0x81, 0x1e, 0xd4, 0xf7, + 0x33, 0xb8, 0x7a, 0x5d, 0x39, 0xc6, 0x72, 0xe5, 0x18, 0x1f, 0x2b, 0xc7, 0x78, 0x5e, 0x3b, 0xa5, + 0xe5, 0xda, 0x29, 0xbd, 0xaf, 0x9d, 0xd2, 0xed, 0x39, 0xe3, 0x6a, 0x9a, 0x4c, 0xbc, 0x00, 0x1e, + 0xf0, 0xb5, 0x4e, 0x0f, 0xa7, 0x84, 0x8b, 0xad, 0x69, 0xd6, 0xc7, 0x8b, 0x4c, 0xa7, 0x1e, 0x23, + 0x2a, 0x27, 0x55, 0xfd, 0x7b, 0x5c, 0x7c, 0x07, 0x00, 0x00, 0xff, 0xff, 0xb6, 0xdb, 0xcd, 0x34, + 0x7d, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/tokenfactory/cli/cli_test.go b/x/tokenfactory/cli/cli_test.go index f1f1e37e1..7680e99aa 100644 --- a/x/tokenfactory/cli/cli_test.go +++ b/x/tokenfactory/cli/cli_test.go @@ -6,46 +6,51 @@ import ( "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil" - testutilcli "github.com/NibiruChain/nibiru/x/common/testutil/cli" - "github.com/NibiruChain/nibiru/x/common/testutil/genesis" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/tokenfactory/cli" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/genesis" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testnetwork" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/cli" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" sdk "github.com/cosmos/cosmos-sdk/types" ) -var _ suite.SetupAllSuite = (*IntegrationTestSuite)(nil) +var ( + _ suite.SetupAllSuite = (*TestSuite)(nil) + _ suite.TearDownAllSuite = (*TestSuite)(nil) +) -type IntegrationTestSuite struct { +type TestSuite struct { suite.Suite - cfg testutilcli.Config - network *testutilcli.Network - val *testutilcli.Validator + cfg testnetwork.Config + network *testnetwork.Network + val *testnetwork.Validator } func TestIntegrationTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) + testutil.RetrySuiteRunIfDbClosed(t, func() { + suite.Run(t, new(TestSuite)) + }, 2) } // TestTokenFactory: Runs the test suite with a deterministic order. -func (s *IntegrationTestSuite) TestTokenFactory() { +func (s *TestSuite) TestTokenFactory() { s.Run("CreateDenomTest", s.CreateDenomTest) s.Run("MintBurnTest", s.MintBurnTest) s.Run("ChangeAdminTest", s.ChangeAdminTest) } -func (s *IntegrationTestSuite) SetupSuite() { +func (s *TestSuite) SetupSuite() { testutil.BeforeIntegrationSuite(s.T()) testapp.EnsureNibiruPrefix() encodingConfig := app.MakeEncodingConfig() genState := genesis.NewTestGenesisState(encodingConfig) - cfg := testutilcli.BuildNetworkConfig(genState) + cfg := testnetwork.BuildNetworkConfig(genState) cfg.NumValidators = 1 - network, err := testutilcli.New(s.T(), s.T().TempDir(), cfg) + network, err := testnetwork.New(s.T(), s.T().TempDir(), cfg) s.NoError(err) s.cfg = cfg @@ -54,7 +59,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.NoError(s.network.WaitForNextBlock()) } -func (s *IntegrationTestSuite) CreateDenomTest() { +func (s *TestSuite) CreateDenomTest() { creator := s.val.Address createDenom := func(subdenom string, wantErr bool) { _, err := s.network.ExecTxCmd( @@ -81,14 +86,14 @@ func (s *IntegrationTestSuite) CreateDenomTest() { ) denoms := denomResp.Denoms wantDenoms := []string{ - types.TFDenom{Creator: creator.String(), Subdenom: "nusd"}.String(), - types.TFDenom{Creator: creator.String(), Subdenom: "stnibi"}.String(), - types.TFDenom{Creator: creator.String(), Subdenom: "stnusd"}.String(), + types.TFDenom{Creator: creator.String(), Subdenom: "nusd"}.Denom().String(), + types.TFDenom{Creator: creator.String(), Subdenom: "stnibi"}.Denom().String(), + types.TFDenom{Creator: creator.String(), Subdenom: "stnusd"}.Denom().String(), } s.ElementsMatch(denoms, wantDenoms) } -func (s *IntegrationTestSuite) MintBurnTest() { +func (s *TestSuite) MintBurnTest() { creator := s.val.Address mint := func(coin string, mintTo string, wantErr bool) { mintToArg := fmt.Sprintf("--mint-to=%s", mintTo) @@ -120,7 +125,7 @@ func (s *IntegrationTestSuite) MintBurnTest() { Creator: creator.String(), Subdenom: "nusd", } - coin := sdk.NewInt64Coin(denom.String(), 420) + coin := sdk.NewInt64Coin(denom.Denom().String(), 420) wantErr := false mint(coin.String(), creator.String(), wantErr) // happy @@ -135,17 +140,17 @@ func (s *IntegrationTestSuite) MintBurnTest() { burn("notacoin_231,,", creator.String(), wantErr) t.Log(`want error: unable to parse "mint-to" or "burn-from"`) - coin.Denom = denom.String() + coin.Denom = denom.Denom().String() mint(coin.String(), "invalidAddr", wantErr) burn(coin.String(), "invalidAddr", wantErr) t.Log("burn successfully") - coin.Denom = denom.String() + coin.Denom = denom.Denom().String() wantErr = false burn(coin.String(), creator.String(), wantErr) // happy } -func (s *IntegrationTestSuite) ChangeAdminTest() { +func (s *TestSuite) ChangeAdminTest() { creator := s.val.Address admin := creator newAdmin := testutil.AccAddress() @@ -158,7 +163,7 @@ func (s *IntegrationTestSuite) ChangeAdminTest() { infoResp := new(types.QueryDenomInfoResponse) s.NoError( s.network.ExecQuery( - cli.NewQueryCmd(), []string{"denom-info", denom.String()}, infoResp, + cli.NewQueryCmd(), []string{"denom-info", denom.Denom().String()}, infoResp, ), ) s.Equal(infoResp.Admin, admin.String()) @@ -166,20 +171,20 @@ func (s *IntegrationTestSuite) ChangeAdminTest() { s.T().Log("Change to a new admin") _, err := s.network.ExecTxCmd( cli.NewTxCmd(), - admin, []string{"change-admin", denom.String(), newAdmin.String()}) + admin, []string{"change-admin", denom.Denom().String(), newAdmin.String()}) s.Require().NoError(err) s.T().Log("Verify new admin is in state") infoResp = new(types.QueryDenomInfoResponse) s.NoError( s.network.ExecQuery( - cli.NewQueryCmd(), []string{"denom-info", denom.String()}, infoResp, + cli.NewQueryCmd(), []string{"denom-info", denom.Denom().String()}, infoResp, ), ) s.Equal(infoResp.Admin, newAdmin.String()) } -func (s *IntegrationTestSuite) TestQueryModuleParams() { +func (s *TestSuite) TestQueryModuleParams() { paramResp := new(types.QueryParamsResponse) s.NoError( s.network.ExecQuery( @@ -189,7 +194,7 @@ func (s *IntegrationTestSuite) TestQueryModuleParams() { s.Equal(paramResp.Params, types.DefaultModuleParams()) } -func (s *IntegrationTestSuite) TearDownSuite() { +func (s *TestSuite) TearDownSuite() { s.T().Log("tearing down integration test suite") s.network.Cleanup() } diff --git a/x/tokenfactory/cli/query.go b/x/tokenfactory/cli/query.go index d67e46154..d8370ed89 100644 --- a/x/tokenfactory/cli/query.go +++ b/x/tokenfactory/cli/query.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" diff --git a/x/tokenfactory/cli/tx.go b/x/tokenfactory/cli/tx.go index 7c472b2cc..21d129109 100644 --- a/x/tokenfactory/cli/tx.go +++ b/x/tokenfactory/cli/tx.go @@ -11,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) // NewTxCmd returns the transaction commands for this module diff --git a/x/tokenfactory/fixture/fixture.go b/x/tokenfactory/fixture/fixture.go index 5a16f682c..cdaca74d2 100644 --- a/x/tokenfactory/fixture/fixture.go +++ b/x/tokenfactory/fixture/fixture.go @@ -1,6 +1,7 @@ package fixture const ( - // WASM_NIBI_STARGATE is a compiled version of: https://github.com/NibiruChain/cw-nibiru/blob/main/contracts/nibi-stargate/src/contract.rs + // WASM_NIBI_STARGATE is a compiled version of: + // https://github.com/NibiruChain/nibiru-wasm/blob/main/contracts/nibi-stargate/src/contract.rs WASM_NIBI_STARGATE = "nibi_stargate.wasm" ) diff --git a/x/tokenfactory/keeper/genesis.go b/x/tokenfactory/keeper/genesis.go index 488861333..389c6b7cd 100644 --- a/x/tokenfactory/keeper/genesis.go +++ b/x/tokenfactory/keeper/genesis.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) // InitGenesis initializes the tokenfactory module's state from a provided genesis @@ -36,13 +36,13 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { denom := iter.Value() authorityMetadata, err := k.Store.GetDenomAuthorityMetadata( - ctx, denom.String()) + ctx, denom.Denom().String()) if err != nil { panic(err) } genDenoms = append(genDenoms, types.GenesisDenom{ - Denom: denom.String(), + Denom: denom.Denom().String(), AuthorityMetadata: authorityMetadata, }) } diff --git a/x/tokenfactory/keeper/genesis_test.go b/x/tokenfactory/keeper/genesis_test.go index 7ead2f7e8..2ef27e537 100644 --- a/x/tokenfactory/keeper/genesis_test.go +++ b/x/tokenfactory/keeper/genesis_test.go @@ -1,8 +1,8 @@ package keeper_test import ( - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) func (s *TestSuite) TestGenesis() { @@ -13,7 +13,7 @@ func (s *TestSuite) TestGenesis() { Subdenom: testutil.RandLetters(3), } s.Require().NoError(denom.Validate()) - return denom.String() + return denom.Denom().String() } testCases := []struct { diff --git a/x/tokenfactory/keeper/grpc_query.go b/x/tokenfactory/keeper/grpc_query.go index 98070f8a2..63bd3bbea 100644 --- a/x/tokenfactory/keeper/grpc_query.go +++ b/x/tokenfactory/keeper/grpc_query.go @@ -3,7 +3,7 @@ package keeper import ( "context" - types "github.com/NibiruChain/nibiru/x/tokenfactory/types" + types "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/x/tokenfactory/keeper/grpc_query_test.go b/x/tokenfactory/keeper/grpc_query_test.go index a9a2ff5b3..0688f59d0 100644 --- a/x/tokenfactory/keeper/grpc_query_test.go +++ b/x/tokenfactory/keeper/grpc_query_test.go @@ -3,8 +3,8 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) func (s *TestSuite) TestQueryModuleParams() { @@ -45,11 +45,11 @@ func (s *TestSuite) TestQueryDenoms() { denomsResp, err := queryDenoms(denom.Creator) s.NoError(err) s.ElementsMatch(denomsResp.Denoms, []string{ - denom.String(), + denom.Denom().String(), types.TFDenom{ Creator: denom.Creator, Subdenom: "foobar", - }.String(), + }.Denom().String(), }) denomsResp, err = queryDenoms("creator") @@ -91,7 +91,7 @@ func (s *TestSuite) TestQueryDenomInfo() { s.Run("case: happy", func() { resp, err := s.querier.DenomInfo(s.GoCtx(), &types.QueryDenomInfoRequest{ - Denom: denom.String(), + Denom: denom.Denom().String(), }) s.NoError(err) s.Equal(creator.String(), resp.Admin) diff --git a/x/tokenfactory/keeper/keeper.go b/x/tokenfactory/keeper/keeper.go index bc2908d16..ab3ec7c56 100644 --- a/x/tokenfactory/keeper/keeper.go +++ b/x/tokenfactory/keeper/keeper.go @@ -11,7 +11,7 @@ import ( "github.com/NibiruChain/collections" - tftypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + tftypes "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) // Keeper of this module maintains collections of feeshares for contracts diff --git a/x/tokenfactory/keeper/keeper_test.go b/x/tokenfactory/keeper/keeper_test.go index fc7a44431..99997c6b5 100644 --- a/x/tokenfactory/keeper/keeper_test.go +++ b/x/tokenfactory/keeper/keeper_test.go @@ -7,11 +7,11 @@ import ( "github.com/stretchr/testify/suite" - tfkeeper "github.com/NibiruChain/nibiru/x/tokenfactory/keeper" - tftypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + tfkeeper "github.com/NibiruChain/nibiru/v2/x/tokenfactory/keeper" + tftypes "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/x/tokenfactory/keeper/msg_server.go b/x/tokenfactory/keeper/msg_server.go index 8bf814768..32083b2c0 100644 --- a/x/tokenfactory/keeper/msg_server.go +++ b/x/tokenfactory/keeper/msg_server.go @@ -5,15 +5,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - grpccodes "google.golang.org/grpc/codes" - grpcstatus "google.golang.org/grpc/status" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/common" + + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) var _ types.MsgServer = (*Keeper)(nil) -var errNilMsg error = grpcstatus.Errorf(grpccodes.InvalidArgument, "nil msg") +var errNilMsg error = common.ErrNilGrpcMsg func (k Keeper) CreateDenom( goCtx context.Context, txMsg *types.MsgCreateDenom, @@ -22,7 +22,7 @@ func (k Keeper) CreateDenom( return resp, errNilMsg } if err := txMsg.ValidateBasic(); err != nil { - return resp, err // ValidateBasic needs to be guaranteed for Wasm bindings + return resp, err } ctx := sdk.UnwrapSDKContext(goCtx) @@ -36,7 +36,7 @@ func (k Keeper) CreateDenom( } return &types.MsgCreateDenomResponse{ - NewTokenDenom: denom.String(), + NewTokenDenom: denom.Denom().String(), }, err } @@ -47,7 +47,7 @@ func (k Keeper) ChangeAdmin( return resp, errNilMsg } if err := txMsg.ValidateBasic(); err != nil { - return resp, err // ValidateBasic needs to be guaranteed for Wasm bindings + return resp, err } ctx := sdk.UnwrapSDKContext(goCtx) @@ -81,7 +81,7 @@ func (k Keeper) UpdateModuleParams( return resp, errNilMsg } if err := txMsg.ValidateBasic(); err != nil { - return resp, err // ValidateBasic needs to be guaranteed for Wasm bindings + return resp, err } if k.authority != txMsg.Authority { @@ -105,7 +105,7 @@ func (k Keeper) Mint( return resp, errNilMsg } if err := txMsg.ValidateBasic(); err != nil { - return resp, err // ValidateBasic needs to be guaranteed for Wasm bindings + return resp, err } ctx := sdk.UnwrapSDKContext(goCtx) @@ -178,7 +178,7 @@ func (k Keeper) Burn( return resp, errNilMsg } if err := txMsg.ValidateBasic(); err != nil { - return resp, err // ValidateBasic needs to be guaranteed for Wasm bindings + return resp, err } ctx := sdk.UnwrapSDKContext(goCtx) @@ -248,7 +248,7 @@ func (k Keeper) SetDenomMetadata( return resp, errNilMsg } if err := txMsg.ValidateBasic(); err != nil { - return resp, err // ValidateBasic needs to be guaranteed for Wasm bindings + return resp, err } ctx := sdk.UnwrapSDKContext(goCtx) diff --git a/x/tokenfactory/keeper/msg_server_test.go b/x/tokenfactory/keeper/msg_server_test.go index 70801cdc4..a403a1618 100644 --- a/x/tokenfactory/keeper/msg_server_test.go +++ b/x/tokenfactory/keeper/msg_server_test.go @@ -4,13 +4,14 @@ import ( "github.com/NibiruChain/collections" sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/math" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/testutil" - oracletypes "github.com/NibiruChain/nibiru/x/oracle/types" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + oracletypes "github.com/NibiruChain/nibiru/v2/x/oracle/types" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) func (s *TestSuite) TestCreateDenom() { @@ -98,8 +99,8 @@ func (s *TestSuite) TestCreateDenom() { want := types.TFDenom{ Creator: tc.txMsg.Sender, Subdenom: tc.txMsg.Subdenom, - }.String() - s.Equal(want, resp.NewTokenDenom) + }.Denom() + s.Equal(want.String(), resp.NewTokenDenom) if tc.postHook != nil { tc.postHook(s.ctx, s.app) @@ -136,7 +137,7 @@ func (s *TestSuite) TestChangeAdmin() { Name: "sad: non-admin tries to change admin", txMsg: &types.MsgChangeAdmin{ Sender: testutil.AccAddress().String(), - Denom: types.TFDenom{Creator: sbf, Subdenom: "ftt"}.String(), + Denom: types.TFDenom{Creator: sbf, Subdenom: "ftt"}.Denom().String(), NewAdmin: testutil.AccAddress().String(), }, wantErr: "only the current admin can set a new admin", @@ -155,7 +156,7 @@ func (s *TestSuite) TestChangeAdmin() { Name: "happy: SBF changes FTT admin", txMsg: &types.MsgChangeAdmin{ Sender: sbf, - Denom: types.TFDenom{Creator: sbf, Subdenom: "ftt"}.String(), + Denom: types.TFDenom{Creator: sbf, Subdenom: "ftt"}.Denom().String(), NewAdmin: testutil.AccAddress().String(), }, wantErr: "", @@ -174,7 +175,7 @@ func (s *TestSuite) TestChangeAdmin() { Name: "sad: change admin for denom that doesn't exist ", txMsg: &types.MsgChangeAdmin{ Sender: sbf, - Denom: types.TFDenom{Creator: sbf, Subdenom: "ftt"}.String(), + Denom: types.TFDenom{Creator: sbf, Subdenom: "ftt"}.Denom().String(), NewAdmin: testutil.AccAddress().String(), }, wantErr: collections.ErrNotFound.Error(), @@ -329,8 +330,8 @@ func (s *TestSuite) TestMintBurn() { Subdenom: "nusd", } nusd69420 := sdk.Coin{ - Denom: tfdenom.String(), - Amount: sdk.NewInt(69_420), + Denom: tfdenom.Denom().String(), + Amount: math.NewInt(69_420), } testCases := []TestCaseTx{ @@ -348,8 +349,8 @@ func (s *TestSuite) TestMintBurn() { Denom: types.TFDenom{ Creator: addrs[0].String(), Subdenom: "nusd", - }.String(), - Amount: sdk.NewInt(69_420), + }.Denom().String(), + Amount: math.NewInt(69_420), }, MintTo: "", }, @@ -362,8 +363,8 @@ func (s *TestSuite) TestMintBurn() { Denom: types.TFDenom{ Creator: addrs[0].String(), Subdenom: "nusd", - }.String(), - Amount: sdk.NewInt(1), + }.Denom().String(), + Amount: math.NewInt(1), }, BurnFrom: "", }, @@ -382,14 +383,14 @@ func (s *TestSuite) TestMintBurn() { s.T().Log("Total supply should decrease by burned amount.") denom := allDenoms[0] s.Equal( - sdk.NewInt(69_419), s.app.BankKeeper.GetSupply(s.ctx, denom.String()).Amount, + math.NewInt(69_419), s.app.BankKeeper.GetSupply(s.ctx, denom.Denom().String()).Amount, ) s.T().Log("Module account should be empty.") coin := s.app.BankKeeper.GetBalance( - s.ctx, tfModuleAddr, denom.String()) + s.ctx, tfModuleAddr, denom.Denom().String()) s.Equal( - sdk.NewInt(0), + math.NewInt(0), coin.Amount, ) }, @@ -434,7 +435,7 @@ func (s *TestSuite) TestMintBurn() { &types.MsgChangeAdmin{ Sender: addrs[0].String(), - Denom: tfdenom.String(), + Denom: tfdenom.Denom().String(), NewAdmin: addrs[1].String(), }, }, @@ -532,13 +533,13 @@ func (s *TestSuite) TestSetDenomMetadata() { Description: "US Dollar", DenomUnits: []*banktypes.DenomUnit{ { - Denom: tfdenom.String(), + Denom: tfdenom.Denom().String(), Exponent: 0, Aliases: []string{"unusd"}, }, {Denom: "USD", Exponent: 6}, }, - Base: tfdenom.String(), + Base: tfdenom.Denom().String(), Display: "USD", Name: "USD", Symbol: "USD", @@ -646,7 +647,7 @@ func (s *TestSuite) TestBurnNative() { Name: "happy: burn", SetupMsgs: []sdk.Msg{}, PreHook: func(ctx sdk.Context, bapp *app.NibiruApp) { - coins := sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(123))) + coins := sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(123))) s.NoError(bapp.BankKeeper.MintCoins(ctx, types.ModuleName, coins)) s.NoError(bapp.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addrs[0], coins)) }, @@ -654,23 +655,23 @@ func (s *TestSuite) TestBurnNative() { { TestMsg: &types.MsgBurnNative{ Sender: addrs[0].String(), - Coin: sdk.NewCoin("unibi", sdk.NewInt(123)), + Coin: sdk.NewCoin("unibi", math.NewInt(123)), }, WantErr: "", }, }, PostHook: func(ctx sdk.Context, bapp *app.NibiruApp) { s.Equal( - sdk.NewInt(0), s.app.BankKeeper.GetSupply(s.ctx, "unibi").Amount, + sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction), s.app.BankKeeper.GetSupply(s.ctx, "unibi").Amount, ) s.Equal( - sdk.NewInt(0), + math.NewInt(0), s.app.BankKeeper.GetBalance(s.ctx, tfModuleAddr, "unibi").Amount, ) s.Equal( - sdk.NewInt(0), + math.NewInt(0), s.app.BankKeeper.GetBalance(s.ctx, addrs[0], "unibi").Amount, ) }, @@ -680,7 +681,7 @@ func (s *TestSuite) TestBurnNative() { Name: "sad: not enough funds", SetupMsgs: []sdk.Msg{}, PreHook: func(ctx sdk.Context, bapp *app.NibiruApp) { - coins := sdk.NewCoins(sdk.NewCoin("unibi", sdk.NewInt(123))) + coins := sdk.NewCoins(sdk.NewCoin("unibi", math.NewInt(123))) s.NoError(bapp.BankKeeper.MintCoins(ctx, types.ModuleName, coins)) s.NoError(bapp.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addrs[0], coins)) }, @@ -688,18 +689,18 @@ func (s *TestSuite) TestBurnNative() { { TestMsg: &types.MsgBurnNative{ Sender: addrs[0].String(), - Coin: sdk.NewCoin("unibi", sdk.NewInt(124)), + Coin: sdk.NewCoin("unibi", math.NewInt(124)), }, WantErr: "spendable balance 123unibi is smaller than 124unibi: insufficient funds", }, }, PostHook: func(ctx sdk.Context, bapp *app.NibiruApp) { s.Equal( - sdk.NewInt(123), s.app.BankKeeper.GetSupply(s.ctx, "unibi").Amount, + math.NewInt(123).Add(sdk.TokensFromConsensusPower(100_000_001, sdk.DefaultPowerReduction)), s.app.BankKeeper.GetSupply(s.ctx, "unibi").Amount, ) s.Equal( - sdk.NewInt(123), + math.NewInt(123), s.app.BankKeeper.GetBalance(s.ctx, addrs[0], "unibi").Amount, ) }, diff --git a/x/tokenfactory/keeper/store.go b/x/tokenfactory/keeper/store.go index c0d7db2bf..60e5a6eb1 100644 --- a/x/tokenfactory/keeper/store.go +++ b/x/tokenfactory/keeper/store.go @@ -6,7 +6,7 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - tftypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + tftypes "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) // StoreAPI isolates the collections for the x/tokenfactory module. @@ -34,17 +34,17 @@ func (api StoreAPI) InsertDenom( return err } // The x/bank keeper is the source of truth. - key := denom.String() + key := denom.Denom() found := api.HasDenom(ctx, denom) if found { - return tftypes.ErrDenomAlreadyRegistered.Wrap(key) + return tftypes.ErrDenomAlreadyRegistered.Wrap(key.String()) } admin := denom.Creator api.unsafeInsertDenom(ctx, denom, admin) api.bankKeeper.SetDenomMetaData(ctx, denom.DefaultBankMetadata()) - api.denomAdmins.Insert(ctx, key, tftypes.DenomAuthorityMetadata{ + api.denomAdmins.Insert(ctx, key.String(), tftypes.DenomAuthorityMetadata{ Admin: admin, }) return nil @@ -55,15 +55,15 @@ func (api StoreAPI) InsertDenom( func (api StoreAPI) unsafeInsertDenom( ctx sdk.Context, denom tftypes.TFDenom, admin string, ) { - denomStr := denom.String() - api.Denoms.Insert(ctx, denomStr, denom) + denomStr := denom.Denom() + api.Denoms.Insert(ctx, denomStr.String(), denom) api.creator.Insert(ctx, denom.Creator) api.bankKeeper.SetDenomMetaData(ctx, denom.DefaultBankMetadata()) - api.denomAdmins.Insert(ctx, denomStr, tftypes.DenomAuthorityMetadata{ + api.denomAdmins.Insert(ctx, denomStr.String(), tftypes.DenomAuthorityMetadata{ Admin: admin, }) _ = ctx.EventManager().EmitTypedEvent(&tftypes.EventCreateDenom{ - Denom: denomStr, + Denom: denomStr.String(), Creator: denom.Creator, }) } @@ -84,7 +84,7 @@ func (api StoreAPI) unsafeGenesisInsertDenom( func (api StoreAPI) HasDenom( ctx sdk.Context, denom tftypes.TFDenom, ) bool { - _, found := api.bankKeeper.GetDenomMetaData(ctx, denom.String()) + _, found := api.bankKeeper.GetDenomMetaData(ctx, denom.Denom().String()) return found } diff --git a/x/tokenfactory/keeper/store_test.go b/x/tokenfactory/keeper/store_test.go index 23c716fc1..e9ec63cc8 100644 --- a/x/tokenfactory/keeper/store_test.go +++ b/x/tokenfactory/keeper/store_test.go @@ -3,9 +3,9 @@ package keeper_test import ( "github.com/NibiruChain/collections" - "github.com/NibiruChain/nibiru/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" - tftypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + tftypes "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) func (s *TestSuite) TestStoreWrite() { @@ -31,7 +31,7 @@ func (s *TestSuite) TestStoreWrite() { s.NoError(tfdenom.Validate(), tfdenom) // query by denom should fail for all denoms - _, err := api.Denoms.Get(s.ctx, tfdenom.String()) + _, err := api.Denoms.Get(s.ctx, tfdenom.Denom().String()) s.Error(err) // query by creator should fail for all addrs diff --git a/x/tokenfactory/keeper/wasm_test.go b/x/tokenfactory/keeper/wasm_test.go index 3d90c750f..f25cfd2d2 100644 --- a/x/tokenfactory/keeper/wasm_test.go +++ b/x/tokenfactory/keeper/wasm_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + "cosmossdk.io/math" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" "github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -18,12 +19,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/app" - "github.com/NibiruChain/nibiru/x/common/denoms" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - "github.com/NibiruChain/nibiru/x/tokenfactory/fixture" - tftypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/x/common/denoms" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/fixture" + tftypes "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) // Instantiate is a empty struct type with conventience functions for @@ -66,7 +67,7 @@ func SetupContracts( ) map[string]LiveContract { wasmName := fixture.WASM_NIBI_STARGATE codeId := StoreContract(t, wasmName, ctx, nibiru, sender) - deposit := sdk.NewCoins(sdk.NewCoin(denoms.NIBI, sdk.OneInt())) + deposit := sdk.NewCoins(sdk.NewCoin(denoms.NIBI, math.OneInt())) contract := Instantiate.ContractNibiStargate(t, ctx, nibiru, codeId, sender, deposit) LiveContracts[wasmName] = LiveContract{ CodeId: codeId, @@ -160,7 +161,7 @@ func (s *TestSuite) TestStargate() { denoms := s.app.TokenFactoryKeeper.QueryDenoms(s.ctx, contract.Addr.String(), ) - s.ElementsMatch(denoms, []string{tfdenom.String()}) + s.ElementsMatch(denoms, []string{tfdenom.Denom().String()}) }) someoneElse := testutil.AccAddress() @@ -172,12 +173,12 @@ func (s *TestSuite) TestStargate() { "mint_to": "%s" } } - `, tfdenom, someoneElse), " ") + `, tfdenom.Denom(), someoneElse), " ") _, err := s.ExecuteAgainstContract(contract, execMsgJson) s.NoError(err, "execMsgJson: %v", execMsgJson) - balance := s.app.BankKeeper.GetBalance(s.ctx, someoneElse, tfdenom.String()) - s.Equal(sdk.NewInt(69_420), balance.Amount) + balance := s.app.BankKeeper.GetBalance(s.ctx, someoneElse, tfdenom.Denom().String()) + s.Equal(math.NewInt(69_420), balance.Amount) }) s.Run("burn from smart contract", func() { @@ -188,12 +189,12 @@ func (s *TestSuite) TestStargate() { "burn_from": "%s" } } - `, tfdenom, someoneElse), " ") + `, tfdenom.Denom(), someoneElse), " ") _, err := s.ExecuteAgainstContract(contract, execMsgJson) s.NoError(err, "execMsgJson: %v", execMsgJson) - balance := s.app.BankKeeper.GetBalance(s.ctx, someoneElse, tfdenom.String()) - s.Equal(sdk.NewInt(420), balance.Amount) + balance := s.app.BankKeeper.GetBalance(s.ctx, someoneElse, tfdenom.Denom().String()) + s.Equal(math.NewInt(420), balance.Amount) }) s.Run("change admin from smart contract", func() { @@ -204,12 +205,12 @@ func (s *TestSuite) TestStargate() { "new_admin": "%s" } } - `, tfdenom, someoneElse), " ") + `, tfdenom.Denom(), someoneElse), " ") _, err := s.ExecuteAgainstContract(contract, execMsgJson) s.NoError(err, "execMsgJson: %v", execMsgJson) denomInfo, err := s.app.TokenFactoryKeeper.QueryDenomInfo( - s.ctx, tfdenom.String(), + s.ctx, tfdenom.Denom().String(), ) s.NoError(err) s.Equal(someoneElse.String(), denomInfo.Admin) @@ -267,7 +268,7 @@ func (s *TestSuite) TestStargateSerde() { for _, tc := range testCases { s.Run(tc.typeUrl, func() { pbMsg, _ := (tc.sdkMsg).(codec.ProtoMarshaler) - sgMsgValue := s.encConfig.Marshaler.MustMarshal(pbMsg) + sgMsgValue := s.encConfig.Codec.MustMarshal(pbMsg) sgMsg := wasmvmtypes.StargateMsg{ TypeURL: tc.typeUrl, Value: sgMsgValue, @@ -280,7 +281,7 @@ func (s *TestSuite) TestStargateSerde() { ibcTransferPort := wasmtesting.MockIBCTransferKeeper{ GetPortFn: func(ctx sdk.Context) string { return "myTransferPort" }, } - wasmEncoders := wasmkeeper.DefaultEncoders(s.encConfig.Marshaler, ibcTransferPort) + wasmEncoders := wasmkeeper.DefaultEncoders(s.encConfig.Codec, ibcTransferPort) mockContractAddr := testutil.AccAddress() sdkMsgs, err := wasmEncoders.Encode(s.ctx, mockContractAddr, "mock-ibc-port", wasmvmtypes.CosmosMsg{ diff --git a/x/tokenfactory/module.go b/x/tokenfactory/module.go index fdf2ce75b..8bdc247ef 100644 --- a/x/tokenfactory/module.go +++ b/x/tokenfactory/module.go @@ -14,27 +14,28 @@ import ( "encoding/json" "fmt" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/spf13/cobra" - abci "github.com/cometbft/cometbft/abci/types" - sdkclient "github.com/cosmos/cosmos-sdk/client" sdkcodec "github.com/cosmos/cosmos-sdk/codec" sdkcodectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" - modulecli "github.com/NibiruChain/nibiru/x/tokenfactory/cli" - modulekeeper "github.com/NibiruChain/nibiru/x/tokenfactory/keeper" - moduletypes "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/cli" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/keeper" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/simulation" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) // type check to ensure the interface is properly implemented var ( _ module.AppModuleBasic = AppModuleBasic{} _ module.AppModule = AppModule{} + _ module.AppModuleSimulation = AppModule{} _ module.BeginBlockAppModule = AppModule{} _ module.EndBlockAppModule = AppModule{} ) @@ -47,13 +48,13 @@ type AppModuleBasic struct{} // Name returns the fees module's name. func (AppModuleBasic) Name() string { - return moduletypes.ModuleName + return types.ModuleName } // RegisterLegacyAminoCodec performs a no-op as the fees do not support Amino // encoding. func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *sdkcodec.LegacyAmino) { - moduletypes.RegisterLegacyAminoCodec(cdc) + types.RegisterLegacyAminoCodec(cdc) } // ConsensusVersion returns the consensus state-breaking version for the module. @@ -66,22 +67,22 @@ func (AppModuleBasic) ConsensusVersion() uint64 { func (AppModuleBasic) RegisterInterfaces( interfaceRegistry sdkcodectypes.InterfaceRegistry, ) { - moduletypes.RegisterInterfaces(interfaceRegistry) + types.RegisterInterfaces(interfaceRegistry) } // DefaultGenesis returns default genesis state as raw bytes for the fees // module. func (AppModuleBasic) DefaultGenesis(cdc sdkcodec.JSONCodec) json.RawMessage { - return cdc.MustMarshalJSON(moduletypes.DefaultGenesis()) + return cdc.MustMarshalJSON(types.DefaultGenesis()) } // ValidateGenesis performs genesis state validation for the fees module. func (b AppModuleBasic) ValidateGenesis( cdc sdkcodec.JSONCodec, _ sdkclient.TxEncodingConfig, bz json.RawMessage, ) error { - var genesisState moduletypes.GenesisState + var genesisState types.GenesisState if err := cdc.UnmarshalJSON(bz, &genesisState); err != nil { - return fmt.Errorf("failed to unmarshal %s genesis state: %w", moduletypes.ModuleName, err) + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) } return genesisState.Validate() @@ -92,19 +93,19 @@ func (b AppModuleBasic) ValidateGenesis( func (b AppModuleBasic) RegisterGRPCGatewayRoutes( c sdkclient.Context, serveMux *runtime.ServeMux, ) { - if err := moduletypes.RegisterQueryHandlerClient(context.Background(), serveMux, moduletypes.NewQueryClient(c)); err != nil { + if err := types.RegisterQueryHandlerClient(context.Background(), serveMux, types.NewQueryClient(c)); err != nil { panic(err) } } // GetTxCmd returns the root tx command for the fees module. func (AppModuleBasic) GetTxCmd() *cobra.Command { - return modulecli.NewTxCmd() + return cli.NewTxCmd() } // GetQueryCmd returns the fees module's root query command. func (AppModuleBasic) GetQueryCmd() *cobra.Command { - return modulecli.NewQueryCmd() + return cli.NewQueryCmd() } // ___________________________________________________________________________ @@ -112,13 +113,13 @@ func (AppModuleBasic) GetQueryCmd() *cobra.Command { // AppModule implements the AppModule interface for the fees module. type AppModule struct { AppModuleBasic - keeper modulekeeper.Keeper + keeper keeper.Keeper ak authkeeper.AccountKeeper } // NewAppModule creates a new AppModule Object func NewAppModule( - k modulekeeper.Keeper, + k keeper.Keeper, ak authkeeper.AccountKeeper, ) AppModule { return AppModule{ @@ -130,7 +131,7 @@ func NewAppModule( // Name returns the fees module's name. func (AppModule) Name() string { - return moduletypes.ModuleName + return types.ModuleName } // RegisterInvariants registers the fees module's invariants. @@ -138,14 +139,14 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} // QuerierRoute returns the module's query routing key. func (am AppModule) QuerierRoute() string { - return moduletypes.RouterKey + return types.RouterKey } // RegisterServices registers a GRPC query service to respond to the // module-specific GRPC queries. func (am AppModule) RegisterServices(cfg module.Configurator) { - moduletypes.RegisterMsgServer(cfg.MsgServer(), am.keeper) - moduletypes.RegisterQueryServer( + types.RegisterMsgServer(cfg.MsgServer(), am.keeper) + types.RegisterQueryServer( cfg.QueryServer(), am.keeper.Querier(), ) } @@ -163,7 +164,7 @@ func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.Valid // InitGenesis performs the fees module's genesis initialization. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, cdc sdkcodec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { - var genesisState moduletypes.GenesisState + var genesisState types.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) am.keeper.InitGenesis(ctx, genesisState) @@ -175,3 +176,17 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc sdkcodec.JSONCodec) json. gs := am.keeper.ExportGenesis(ctx) return cdc.MustMarshalJSON(gs) } + +// GenerateGenesisState implements module.AppModuleSimulation. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// RegisterStoreDecoder implements module.AppModuleSimulation. +func (AppModule) RegisterStoreDecoder(sdk.StoreDecoderRegistry) { +} + +// WeightedOperations implements module.AppModuleSimulation. +func (AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return []simtypes.WeightedOperation{} +} diff --git a/x/tokenfactory/module_test.go b/x/tokenfactory/module_test.go index f143c4296..1b50c4c7e 100644 --- a/x/tokenfactory/module_test.go +++ b/x/tokenfactory/module_test.go @@ -6,10 +6,10 @@ import ( abci "github.com/cometbft/cometbft/abci/types" "github.com/stretchr/testify/suite" - "github.com/NibiruChain/nibiru/app/codec" - "github.com/NibiruChain/nibiru/x/common/testutil/testapp" - module "github.com/NibiruChain/nibiru/x/tokenfactory" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/app/codec" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + module "github.com/NibiruChain/nibiru/v2/x/tokenfactory" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) type ModuleTestSuite struct{ suite.Suite } diff --git a/x/tokenfactory/simulation/genesis.go b/x/tokenfactory/simulation/genesis.go new file mode 100644 index 000000000..389e25b5b --- /dev/null +++ b/x/tokenfactory/simulation/genesis.go @@ -0,0 +1,44 @@ +package simulation + +// DONTCOVER + +import ( + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" +) + +const ( + DenomCreationGasConsume = "denom_creation_gas_consume" +) + +func GenDenomCreationGasConsume(r *rand.Rand) uint64 { + return uint64(r.Intn(4e6)) +} + +// RandomizedGenState generates a random GenesisState for distribution +func RandomizedGenState(simState *module.SimulationState) { + var denomCreationGasConsume uint64 + simState.AppParams.GetOrGenerate( + simState.Cdc, DenomCreationGasConsume, &denomCreationGasConsume, simState.Rand, + func(r *rand.Rand) { denomCreationGasConsume = GenDenomCreationGasConsume(r) }, + ) + + tokenfactoryGenesis := types.GenesisState{ + Params: types.ModuleParams{ + DenomCreationGasConsume: denomCreationGasConsume, + }, + FactoryDenoms: []types.GenesisDenom{}, + } + + bz, err := json.MarshalIndent(&tokenfactoryGenesis, "", " ") + if err != nil { + panic(err) + } + fmt.Printf("Selected randomly generated x/tokenfactory parameters:\n%s\n", bz) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&tokenfactoryGenesis) +} diff --git a/x/tokenfactory/types/codec.go b/x/tokenfactory/types/codec.go index e0e639c0d..c20e55ad4 100644 --- a/x/tokenfactory/types/codec.go +++ b/x/tokenfactory/types/codec.go @@ -58,7 +58,7 @@ func TX_MSG_TYPE_URLS() []string { // Amino JSON serialization and EIP-712 compatibility. func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { for _, ele := range []struct { - MsgType interface{} + MsgType any Name string }{ {&MsgCreateDenom{}, "nibiru/tokenfactory/create-denom"}, diff --git a/x/tokenfactory/types/codec_test.go b/x/tokenfactory/types/codec_test.go index 2e0300055..c84a4266d 100644 --- a/x/tokenfactory/types/codec_test.go +++ b/x/tokenfactory/types/codec_test.go @@ -8,7 +8,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) type CodecSuite struct { diff --git a/x/tokenfactory/types/event.pb.go b/x/tokenfactory/types/event.pb.go index 808fb098d..1433be44c 100644 --- a/x/tokenfactory/types/event.pb.go +++ b/x/tokenfactory/types/event.pb.go @@ -332,34 +332,35 @@ func init() { } var fileDescriptor_a46c3c7b7d022093 = []byte{ - // 432 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x92, 0xbf, 0x92, 0xd3, 0x30, - 0x10, 0xc6, 0x6d, 0x38, 0x72, 0xb1, 0xd2, 0x30, 0xe6, 0x38, 0x02, 0x19, 0x7c, 0x8c, 0x2b, 0x2a, - 0x69, 0x1c, 0x3a, 0x1a, 0xe6, 0x1c, 0xe8, 0x38, 0x8a, 0xd0, 0xd1, 0xdc, 0xc8, 0x96, 0xce, 0xd1, - 0xc4, 0xd6, 0xde, 0xc8, 0x8a, 0x8f, 0x74, 0x14, 0x0c, 0x35, 0x8f, 0x95, 0x32, 0x25, 0x55, 0x86, - 0x49, 0xde, 0x80, 0x27, 0x60, 0x24, 0xcb, 0x21, 0x14, 0xa1, 0xa2, 0xd3, 0xee, 0xb7, 0x7f, 0x7e, - 0x9f, 0xbd, 0x28, 0x96, 0x22, 0x13, 0x6a, 0x41, 0x34, 0xcc, 0xb9, 0xbc, 0xa1, 0xb9, 0x06, 0xb5, - 0x24, 0x4d, 0x42, 0x78, 0xc3, 0xa5, 0xc6, 0xb7, 0x0a, 0x34, 0x84, 0xe7, 0x6d, 0x0d, 0x3e, 0xac, - 0xc1, 0x4d, 0xf2, 0x2c, 0xca, 0xa1, 0xae, 0xa0, 0x26, 0x19, 0x95, 0x73, 0xd2, 0x24, 0x19, 0xd7, - 0x34, 0xb1, 0x41, 0xdb, 0x77, 0xa0, 0xd7, 0x7c, 0xaf, 0xe7, 0x20, 0xa4, 0xd3, 0xcf, 0x0a, 0x28, - 0xc0, 0x3e, 0x89, 0x79, 0xb5, 0xd9, 0x38, 0x45, 0x0f, 0xdf, 0x99, 0xe5, 0x13, 0xc5, 0xa9, 0xe6, - 0x6f, 0xb9, 0x84, 0x2a, 0x3c, 0x43, 0x0f, 0x98, 0x79, 0x0c, 0xfd, 0x17, 0xfe, 0xcb, 0x60, 0xda, - 0x06, 0xe1, 0x10, 0x9d, 0xe6, 0xa6, 0x08, 0xd4, 0xf0, 0x9e, 0xcd, 0x77, 0x61, 0x9c, 0x75, 0x33, - 0x66, 0x54, 0x16, 0xfc, 0x92, 0x55, 0x42, 0x1e, 0x99, 0x31, 0x42, 0x81, 0xe4, 0x77, 0xd7, 0xd4, - 0x94, 0xb8, 0x29, 0x7d, 0xc9, 0xef, 0xda, 0x96, 0x11, 0x0a, 0xa0, 0x64, 0x4e, 0xbc, 0xdf, 0x8a, - 0x50, 0x32, 0x2b, 0xc6, 0x5f, 0x7c, 0x14, 0xd8, 0x25, 0x57, 0x42, 0xea, 0x30, 0x45, 0x27, 0xc6, - 0x99, 0x1d, 0x3e, 0x18, 0x3f, 0xc5, 0xad, 0x75, 0x6c, 0xac, 0x63, 0x67, 0x1d, 0x4f, 0x40, 0xc8, - 0xf4, 0xd1, 0x6a, 0x73, 0xe1, 0xfd, 0xda, 0x5c, 0x0c, 0x96, 0xb4, 0x2a, 0x5f, 0xc7, 0xa6, 0x29, - 0x9e, 0xda, 0xde, 0xf0, 0x09, 0x3a, 0xd5, 0x70, 0x4d, 0x19, 0xeb, 0xfc, 0xf4, 0x34, 0x5c, 0x32, - 0xa6, 0xc2, 0x73, 0xd4, 0xcb, 0x69, 0x59, 0x72, 0xe5, 0x20, 0x5c, 0x14, 0x7f, 0xed, 0x10, 0xd2, - 0x85, 0x92, 0xff, 0x05, 0x61, 0x84, 0x82, 0x1b, 0x05, 0xd5, 0x21, 0x44, 0xdf, 0x24, 0xfe, 0x89, - 0xf1, 0xcd, 0x47, 0x8f, 0x2d, 0xc6, 0x47, 0xae, 0xed, 0xff, 0xba, 0xe2, 0x9a, 0x32, 0xaa, 0xe9, - 0x91, 0x6f, 0xfe, 0x06, 0xf5, 0x2b, 0x57, 0x61, 0x77, 0x0c, 0xc6, 0xcf, 0xff, 0xc0, 0xca, 0xf9, - 0x1e, 0xb6, 0x1b, 0x93, 0x9e, 0x18, 0xe0, 0xe9, 0xbe, 0xe9, 0x18, 0x48, 0xfa, 0x7e, 0xb5, 0x8d, - 0xfc, 0xf5, 0x36, 0xf2, 0x7f, 0x6e, 0x23, 0xff, 0xfb, 0x2e, 0xf2, 0xd6, 0xbb, 0xc8, 0xfb, 0xb1, - 0x8b, 0xbc, 0x4f, 0xe3, 0x42, 0xe8, 0xd9, 0x22, 0xc3, 0x39, 0x54, 0xe4, 0x83, 0xbd, 0xe6, 0xc9, - 0x8c, 0x0a, 0x49, 0xdc, 0xf5, 0x7f, 0xfe, 0xfb, 0xfe, 0xf5, 0xf2, 0x96, 0xd7, 0x59, 0xcf, 0xde, - 0xe3, 0xab, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x91, 0x31, 0x11, 0x4b, 0x23, 0x03, 0x00, 0x00, + // 436 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0xbf, 0x8f, 0xd3, 0x30, + 0x14, 0xc7, 0x13, 0x38, 0x7a, 0x8d, 0xbb, 0xa0, 0x70, 0x1c, 0x85, 0x8a, 0x1c, 0xca, 0xc4, 0x64, + 0x2b, 0x45, 0x2c, 0x2c, 0xe8, 0x52, 0x18, 0x0f, 0xa4, 0xb2, 0xb1, 0x9c, 0x9c, 0xd8, 0x97, 0x5a, + 0x4d, 0xfc, 0x4e, 0x8e, 0x9b, 0xa3, 0x1b, 0x03, 0x62, 0xe6, 0xcf, 0xba, 0xb1, 0x23, 0x53, 0x85, + 0xda, 0xff, 0x80, 0xbf, 0x00, 0xd9, 0x71, 0x4a, 0x19, 0xca, 0x74, 0xdb, 0x7b, 0xef, 0xfb, 0x7e, + 0x7c, 0x5e, 0xf2, 0x8c, 0x62, 0x29, 0x32, 0xa1, 0x16, 0x44, 0xc3, 0x9c, 0xcb, 0x2b, 0x9a, 0x6b, + 0x50, 0x4b, 0xd2, 0x24, 0x84, 0x37, 0x5c, 0x6a, 0x7c, 0xad, 0x40, 0x43, 0x78, 0xda, 0xe6, 0xe0, + 0xfd, 0x1c, 0xdc, 0x24, 0xcf, 0xa2, 0x1c, 0xea, 0x0a, 0x6a, 0x92, 0x51, 0x39, 0x27, 0x4d, 0x92, + 0x71, 0x4d, 0x13, 0xeb, 0xb4, 0x75, 0x7b, 0x7a, 0xcd, 0x77, 0x7a, 0x0e, 0x42, 0x3a, 0xfd, 0xa4, + 0x80, 0x02, 0xac, 0x49, 0x8c, 0xd5, 0x46, 0xe3, 0x14, 0x3d, 0x7c, 0x6f, 0x86, 0x4f, 0x14, 0xa7, + 0x9a, 0xbf, 0xe3, 0x12, 0xaa, 0xf0, 0x04, 0x3d, 0x60, 0xc6, 0x18, 0xfa, 0x2f, 0xfc, 0x97, 0xc1, + 0xb4, 0x75, 0xc2, 0x21, 0x3a, 0xce, 0x4d, 0x12, 0xa8, 0xe1, 0x3d, 0x1b, 0xef, 0xdc, 0x38, 0xeb, + 0x7a, 0xcc, 0xa8, 0x2c, 0xf8, 0x39, 0xab, 0x84, 0x3c, 0xd0, 0x63, 0x84, 0x02, 0xc9, 0x6f, 0x2e, + 0xa9, 0x49, 0x71, 0x5d, 0xfa, 0x92, 0xdf, 0xb4, 0x25, 0x23, 0x14, 0x40, 0xc9, 0x9c, 0x78, 0xbf, + 0x15, 0xa1, 0x64, 0x56, 0x8c, 0xbf, 0xfa, 0x28, 0xb0, 0x43, 0x2e, 0x84, 0xd4, 0x61, 0x8a, 0x8e, + 0xcc, 0x66, 0xb6, 0xf9, 0x60, 0xfc, 0x14, 0xb7, 0xab, 0x63, 0xb3, 0x3a, 0x76, 0xab, 0xe3, 0x09, + 0x08, 0x99, 0x3e, 0xba, 0x5d, 0x9f, 0x79, 0xbf, 0xd7, 0x67, 0x83, 0x25, 0xad, 0xca, 0x37, 0xb1, + 0x29, 0x8a, 0xa7, 0xb6, 0x36, 0x7c, 0x82, 0x8e, 0x35, 0x5c, 0x52, 0xc6, 0xba, 0x7d, 0x7a, 0x1a, + 0xce, 0x19, 0x53, 0xe1, 0x29, 0xea, 0xe5, 0xb4, 0x2c, 0xb9, 0x72, 0x10, 0xce, 0x8b, 0xbf, 0x75, + 0x08, 0xe9, 0x42, 0xc9, 0x3b, 0x41, 0x18, 0xa1, 0xe0, 0x4a, 0x41, 0xb5, 0x0f, 0xd1, 0x37, 0x81, + 0xff, 0x62, 0x7c, 0xf7, 0xd1, 0x63, 0x8b, 0xf1, 0x89, 0x6b, 0xfb, 0xbf, 0x2e, 0xb8, 0xa6, 0x8c, + 0x6a, 0x7a, 0xe0, 0x9b, 0xbf, 0x45, 0xfd, 0xca, 0x65, 0xd8, 0x19, 0x83, 0xf1, 0xf3, 0xbf, 0xb0, + 0x72, 0xbe, 0x83, 0xed, 0xda, 0xa4, 0x47, 0x06, 0x78, 0xba, 0x2b, 0x3a, 0x04, 0x92, 0x7e, 0xbc, + 0xdd, 0x44, 0xfe, 0x6a, 0x13, 0xf9, 0xbf, 0x36, 0x91, 0xff, 0x63, 0x1b, 0x79, 0xab, 0x6d, 0xe4, + 0xfd, 0xdc, 0x46, 0xde, 0xe7, 0xd7, 0x85, 0xd0, 0xb3, 0x45, 0x86, 0x73, 0xa8, 0xc8, 0x07, 0x7b, + 0xcd, 0x93, 0x19, 0x15, 0x92, 0xb8, 0xeb, 0x6f, 0xc6, 0xe4, 0xcb, 0xbf, 0x4f, 0x40, 0x2f, 0xaf, + 0x79, 0x9d, 0xf5, 0xec, 0x49, 0xbe, 0xfa, 0x13, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x7c, 0xf8, 0x56, + 0x26, 0x03, 0x00, 0x00, } func (m *EventCreateDenom) Marshal() (dAtA []byte, err error) { diff --git a/x/tokenfactory/types/genesis.go b/x/tokenfactory/types/genesis.go index d97990c3a..7546ceb7b 100644 --- a/x/tokenfactory/types/genesis.go +++ b/x/tokenfactory/types/genesis.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/NibiruChain/nibiru/x/common/set" + "github.com/NibiruChain/nibiru/v2/x/common/set" ) // DefaultGenesis returns the default genesis state. This state is used for diff --git a/x/tokenfactory/types/query.pb.go b/x/tokenfactory/types/query.pb.go index 63b808876..43c75eab8 100644 --- a/x/tokenfactory/types/query.pb.go +++ b/x/tokenfactory/types/query.pb.go @@ -318,38 +318,39 @@ func init() { } var fileDescriptor_b7d8bbc34d6c2a91 = []byte{ - // 495 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x31, 0x6f, 0x13, 0x31, - 0x14, 0xc7, 0xe3, 0x96, 0x1c, 0xc4, 0x6c, 0x26, 0x44, 0x51, 0x04, 0x47, 0x64, 0x21, 0x14, 0x05, - 0x62, 0x93, 0xf0, 0x01, 0x90, 0x02, 0x0b, 0x12, 0x45, 0x90, 0x0d, 0x36, 0x5f, 0xe2, 0x5c, 0x4f, - 0xed, 0xf9, 0x5d, 0xcf, 0xbe, 0x88, 0xa8, 0xea, 0xc2, 0xc6, 0x86, 0xd4, 0x9d, 0xcf, 0x53, 0x31, - 0x55, 0x62, 0x61, 0x42, 0x28, 0xe1, 0x83, 0xa0, 0xda, 0xbe, 0xd0, 0x00, 0x21, 0xdd, 0xfc, 0xec, - 0x9f, 0xdf, 0xff, 0xef, 0xf7, 0xbf, 0xc3, 0x54, 0x25, 0x51, 0x92, 0x17, 0xdc, 0xc0, 0x81, 0x54, - 0x53, 0x31, 0x36, 0x90, 0xcf, 0xf9, 0xac, 0xcf, 0x8f, 0x0a, 0x99, 0xcf, 0x59, 0x96, 0x83, 0x01, - 0xd2, 0x70, 0x0c, 0xbb, 0xcc, 0xb0, 0x59, 0xbf, 0x55, 0x8f, 0x21, 0x06, 0x8b, 0xf0, 0x8b, 0x95, - 0xa3, 0x5b, 0x77, 0x62, 0x80, 0xf8, 0x50, 0x72, 0x91, 0x25, 0x5c, 0x28, 0x05, 0x46, 0x98, 0x04, - 0x94, 0xf6, 0xa7, 0xe1, 0x18, 0x74, 0x0a, 0x9a, 0x47, 0x42, 0x1d, 0xf0, 0x59, 0x3f, 0x92, 0x46, - 0xf4, 0x6d, 0xe1, 0xcf, 0x37, 0xf9, 0xd1, 0x46, 0x18, 0xe9, 0x18, 0x5a, 0xc7, 0xe4, 0xcd, 0x85, - 0xbd, 0xd7, 0x22, 0x17, 0xa9, 0x1e, 0xc9, 0xa3, 0x42, 0x6a, 0x43, 0xdf, 0xe2, 0x5b, 0x6b, 0xbb, - 0x3a, 0x03, 0xa5, 0x25, 0x19, 0xe2, 0x20, 0xb3, 0x3b, 0x4d, 0xd4, 0x46, 0x9d, 0x9b, 0x83, 0xfb, - 0xec, 0xdf, 0xaf, 0x61, 0x7b, 0x30, 0x29, 0x0e, 0xa5, 0xbb, 0x3d, 0xbc, 0x76, 0xf6, 0xfd, 0x5e, - 0x65, 0xe4, 0x6f, 0x52, 0xe6, 0x05, 0x9f, 0x4b, 0x05, 0x2b, 0x41, 0xd2, 0xc4, 0xd7, 0xc7, 0xb9, - 0x14, 0x06, 0x72, 0xdb, 0xba, 0x36, 0x2a, 0x4b, 0xda, 0xf3, 0x56, 0x4a, 0xde, 0x5b, 0x69, 0xe0, - 0x60, 0x62, 0x77, 0x9a, 0xa8, 0xbd, 0xdb, 0xa9, 0x8d, 0x7c, 0x45, 0x7b, 0xf8, 0xf6, 0x6f, 0xfc, - 0x85, 0x9a, 0x42, 0xa9, 0x50, 0xc7, 0x55, 0x8b, 0xf8, 0xfe, 0xae, 0xa0, 0x80, 0x1b, 0x7f, 0xe2, - 0x5e, 0xa0, 0x8e, 0xab, 0x62, 0x92, 0x26, 0xaa, 0xe4, 0x6d, 0x41, 0x9e, 0xe2, 0x1b, 0xa9, 0x34, - 0x62, 0x22, 0x8c, 0x68, 0xee, 0xd8, 0x19, 0xdc, 0x65, 0x2e, 0x05, 0x66, 0x07, 0xef, 0x53, 0x60, - 0x7b, 0x1e, 0xf2, 0x8f, 0x5f, 0x5d, 0x1a, 0x7c, 0xd9, 0xc5, 0x55, 0xab, 0x48, 0x3e, 0x22, 0x1c, - 0xb8, 0x09, 0x91, 0xee, 0xa6, 0x39, 0xfe, 0x1d, 0x4d, 0xeb, 0xe1, 0x95, 0x58, 0xf7, 0x08, 0xfa, - 0xe0, 0xc3, 0xd7, 0x9f, 0xa7, 0x3b, 0x6d, 0x12, 0xf2, 0x0d, 0x9f, 0x82, 0x0b, 0x85, 0x9c, 0x22, - 0x1c, 0xb8, 0x01, 0x6f, 0xf1, 0xb2, 0x96, 0xda, 0x16, 0x2f, 0xeb, 0x89, 0xd1, 0xc7, 0xd6, 0x4b, - 0x97, 0x74, 0x36, 0x79, 0x71, 0x09, 0xf2, 0x63, 0x9f, 0xfc, 0x09, 0xf9, 0x8c, 0x70, 0x6d, 0x15, - 0x0c, 0xe9, 0x6d, 0x17, 0xbb, 0x94, 0x77, 0x8b, 0x5d, 0x15, 0xf7, 0xf6, 0x06, 0xd6, 0xde, 0x23, - 0xd2, 0xfd, 0xaf, 0xbd, 0x5e, 0xa2, 0xa6, 0xc0, 0x8f, 0xed, 0xfa, 0x64, 0xf8, 0xf2, 0x6c, 0x11, - 0xa2, 0xf3, 0x45, 0x88, 0x7e, 0x2c, 0x42, 0xf4, 0x69, 0x19, 0x56, 0xce, 0x97, 0x61, 0xe5, 0xdb, - 0x32, 0xac, 0xbc, 0x1b, 0xc4, 0x89, 0xd9, 0x2f, 0x22, 0x36, 0x86, 0x94, 0xbf, 0xb2, 0xfd, 0x9e, - 0xed, 0x8b, 0x44, 0x95, 0xbd, 0xdf, 0xaf, 0x77, 0x37, 0xf3, 0x4c, 0xea, 0x28, 0xb0, 0x7f, 0xe4, - 0x93, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xed, 0x27, 0xd8, 0x56, 0x47, 0x04, 0x00, 0x00, + // 499 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xbf, 0x6f, 0x13, 0x3f, + 0x18, 0xc6, 0xe3, 0xf6, 0x9b, 0xfb, 0x12, 0xb3, 0x99, 0x10, 0x45, 0x11, 0x1c, 0x91, 0x85, 0x50, + 0x14, 0x88, 0x4d, 0x82, 0x98, 0x91, 0x02, 0x0b, 0x43, 0xf9, 0x91, 0x0d, 0x36, 0x5f, 0xe2, 0x5c, + 0x4f, 0xed, 0xf9, 0xbd, 0x9e, 0x7d, 0x11, 0x51, 0xd5, 0x85, 0x8d, 0x0d, 0xa9, 0x3b, 0x7f, 0x4f, + 0xc5, 0x54, 0x89, 0x85, 0x09, 0xa1, 0x84, 0x3f, 0x04, 0xd5, 0xf6, 0x85, 0x06, 0x08, 0xe9, 0xe6, + 0xd7, 0xfe, 0xf8, 0x7d, 0x1e, 0xbf, 0xcf, 0x1d, 0xa6, 0x2a, 0x89, 0x92, 0xbc, 0xe0, 0x06, 0x0e, + 0xa4, 0x9a, 0x8a, 0xb1, 0x81, 0x7c, 0xce, 0x67, 0x7d, 0x7e, 0x54, 0xc8, 0x7c, 0xce, 0xb2, 0x1c, + 0x0c, 0x90, 0x86, 0x63, 0xd8, 0x65, 0x86, 0xcd, 0xfa, 0xad, 0x7a, 0x0c, 0x31, 0x58, 0x84, 0x5f, + 0xac, 0x1c, 0xdd, 0xba, 0x15, 0x03, 0xc4, 0x87, 0x92, 0x8b, 0x2c, 0xe1, 0x42, 0x29, 0x30, 0xc2, + 0x24, 0xa0, 0xb4, 0x3f, 0x0d, 0xc7, 0xa0, 0x53, 0xd0, 0x3c, 0x12, 0xea, 0x80, 0xcf, 0xfa, 0x91, + 0x34, 0xa2, 0x6f, 0x0b, 0x7f, 0xbe, 0xc9, 0x8f, 0x36, 0xc2, 0x48, 0xc7, 0xd0, 0x3a, 0x26, 0xaf, + 0x2f, 0xec, 0xbd, 0x12, 0xb9, 0x48, 0xf5, 0x48, 0x1e, 0x15, 0x52, 0x1b, 0xfa, 0x06, 0xdf, 0x58, + 0xdb, 0xd5, 0x19, 0x28, 0x2d, 0xc9, 0x10, 0x07, 0x99, 0xdd, 0x69, 0xa2, 0x36, 0xea, 0x5c, 0x1f, + 0xdc, 0x65, 0x7f, 0x7f, 0x0d, 0xdb, 0x83, 0x49, 0x71, 0x28, 0xdd, 0xed, 0xe1, 0x7f, 0x67, 0xdf, + 0xee, 0x54, 0x46, 0xfe, 0x26, 0x65, 0x5e, 0xf0, 0x99, 0x54, 0xb0, 0x12, 0x24, 0x4d, 0xfc, 0xff, + 0x38, 0x97, 0xc2, 0x40, 0x6e, 0x5b, 0xd7, 0x46, 0x65, 0x49, 0x7b, 0xde, 0x4a, 0xc9, 0x7b, 0x2b, + 0x0d, 0x1c, 0x4c, 0xec, 0x4e, 0x13, 0xb5, 0x77, 0x3b, 0xb5, 0x91, 0xaf, 0x68, 0x0f, 0xdf, 0xfc, + 0x85, 0x3f, 0x57, 0x53, 0x28, 0x15, 0xea, 0xb8, 0x6a, 0x11, 0xdf, 0xdf, 0x15, 0x14, 0x70, 0xe3, + 0x77, 0xdc, 0x0b, 0xd4, 0x71, 0x55, 0x4c, 0xd2, 0x44, 0x95, 0xbc, 0x2d, 0xc8, 0x13, 0x7c, 0x2d, + 0x95, 0x46, 0x4c, 0x84, 0x11, 0xcd, 0x1d, 0x3b, 0x83, 0xdb, 0xcc, 0xa5, 0xc0, 0xec, 0xe0, 0x7d, + 0x0a, 0x6c, 0xcf, 0x43, 0xfe, 0xf1, 0xab, 0x4b, 0x83, 0xcf, 0xbb, 0xb8, 0x6a, 0x15, 0xc9, 0x07, + 0x84, 0x03, 0x37, 0x21, 0xd2, 0xdd, 0x34, 0xc7, 0x3f, 0xa3, 0x69, 0xdd, 0xbf, 0x12, 0xeb, 0x1e, + 0x41, 0xef, 0xbd, 0xff, 0xf2, 0xe3, 0x74, 0xa7, 0x4d, 0x42, 0xbe, 0xe1, 0x53, 0x70, 0xa1, 0x90, + 0x53, 0x84, 0x03, 0x37, 0xe0, 0x2d, 0x5e, 0xd6, 0x52, 0xdb, 0xe2, 0x65, 0x3d, 0x31, 0xfa, 0xd0, + 0x7a, 0xe9, 0x92, 0xce, 0x26, 0x2f, 0x2e, 0x41, 0x7e, 0xec, 0x93, 0x3f, 0x21, 0x9f, 0x10, 0xae, + 0xad, 0x82, 0x21, 0xbd, 0xed, 0x62, 0x97, 0xf2, 0x6e, 0xb1, 0xab, 0xe2, 0xde, 0xde, 0xc0, 0xda, + 0x7b, 0x40, 0xba, 0xff, 0xb4, 0xd7, 0x4b, 0xd4, 0x14, 0xf8, 0xb1, 0x5d, 0x9f, 0x0c, 0x5f, 0x9e, + 0x2d, 0x42, 0x74, 0xbe, 0x08, 0xd1, 0xf7, 0x45, 0x88, 0x3e, 0x2e, 0xc3, 0xca, 0xf9, 0x32, 0xac, + 0x7c, 0x5d, 0x86, 0x95, 0xb7, 0x8f, 0xe3, 0xc4, 0xec, 0x17, 0x11, 0x1b, 0x43, 0xca, 0x5f, 0xd8, + 0x7e, 0x4f, 0xf7, 0x45, 0xa2, 0xca, 0xde, 0xb3, 0x01, 0x7f, 0xb7, 0x2e, 0x60, 0xe6, 0x99, 0xd4, + 0x51, 0x60, 0x7f, 0xca, 0x47, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x55, 0x22, 0x33, 0xf5, 0x4a, + 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/tokenfactory/types/state.go b/x/tokenfactory/types/state.go index 782c7c087..1a0f56b15 100644 --- a/x/tokenfactory/types/state.go +++ b/x/tokenfactory/types/state.go @@ -29,33 +29,37 @@ func (params ModuleParams) Validate() error { // ---------------------------------------------------- func (tfd TFDenom) Denom() DenomStr { - return DenomStr(tfd.String()) + return DenomStr(fmt.Sprintf("tf/%s/%s", tfd.Creator, tfd.Subdenom)) } -// String: returns the standard string representation. -func (tfd TFDenom) String() string { - return fmt.Sprintf("tf/%s/%s", tfd.Creator, tfd.Subdenom) -} +// // String: returns the standard string representation. +// func (tfd TFDenom) DenomStr() string { +// return fmt.Sprintf("tf/%s/%s", tfd.Creator, tfd.Subdenom) +// } func (tfd TFDenom) Validate() error { return tfd.Denom().Validate() } func (tfd TFDenom) DefaultBankMetadata() banktypes.Metadata { - denom := tfd.String() + denom := tfd.Denom() return banktypes.Metadata{ DenomUnits: []*banktypes.DenomUnit{{ - Denom: denom, + Denom: denom.String(), Exponent: 0, }}, - Base: denom, + Base: denom.String(), // The following is necessary for x/bank denom validation - Display: denom, - Name: denom, - Symbol: denom, + Display: denom.String(), + Name: denom.String(), + Symbol: denom.String(), } } +func (tfd TFDenom) IsEqual(other TFDenom) bool { + return tfd.Creator == other.Creator && tfd.Subdenom == other.Subdenom +} + // ---------------------------------------------------- // DenomStr functions // ---------------------------------------------------- diff --git a/x/tokenfactory/types/state.pb.go b/x/tokenfactory/types/state.pb.go index 8c4005b78..c1916c787 100644 --- a/x/tokenfactory/types/state.pb.go +++ b/x/tokenfactory/types/state.pb.go @@ -157,8 +157,9 @@ type TFDenom struct { Subdenom string `protobuf:"bytes,2,opt,name=subdenom,proto3" json:"subdenom,omitempty"` } -func (m *TFDenom) Reset() { *m = TFDenom{} } -func (*TFDenom) ProtoMessage() {} +func (m *TFDenom) Reset() { *m = TFDenom{} } +func (m *TFDenom) String() string { return proto.CompactTextString(m) } +func (*TFDenom) ProtoMessage() {} func (*TFDenom) Descriptor() ([]byte, []int) { return fileDescriptor_452ec984f7eef90f, []int{2} } @@ -322,38 +323,38 @@ func init() { } var fileDescriptor_452ec984f7eef90f = []byte{ - // 496 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xbd, 0x6e, 0x13, 0x4d, - 0x14, 0xdd, 0xcd, 0xe7, 0xfc, 0x7c, 0x13, 0x83, 0x60, 0x04, 0xc1, 0xb1, 0xc4, 0xae, 0x33, 0x82, - 0x88, 0x6a, 0x47, 0x36, 0x9d, 0x3b, 0xd6, 0x51, 0x22, 0x24, 0x82, 0xd0, 0x42, 0x45, 0xb3, 0xba, - 0xbb, 0x1e, 0xec, 0x81, 0xec, 0x8c, 0xb5, 0x33, 0x6b, 0xe1, 0x2e, 0x8f, 0x40, 0x49, 0x99, 0x27, - 0x81, 0x36, 0x65, 0x4a, 0x0a, 0x64, 0x21, 0xbb, 0xa1, 0xf6, 0x13, 0x20, 0xcf, 0x4c, 0x20, 0x21, - 0x09, 0xdd, 0xde, 0x7b, 0xce, 0x3d, 0x7b, 0xee, 0xcf, 0x20, 0x22, 0x78, 0xc6, 0xcb, 0x8a, 0x6a, - 0xf9, 0x81, 0x89, 0x77, 0x90, 0x6b, 0x59, 0x4e, 0xe8, 0xb8, 0x4d, 0x95, 0x06, 0xcd, 0xa2, 0x51, - 0x29, 0xb5, 0xc4, 0x5b, 0x96, 0x13, 0x5d, 0xe4, 0x44, 0xe3, 0x76, 0xf3, 0xde, 0x40, 0x0e, 0xa4, - 0xa1, 0xd0, 0xe5, 0x97, 0x65, 0x37, 0xb7, 0x73, 0xa9, 0x0a, 0xa9, 0x52, 0x0b, 0xd8, 0xc0, 0x41, - 0x81, 0x8d, 0x68, 0x06, 0x8a, 0xd1, 0x71, 0x3b, 0x63, 0x1a, 0xda, 0x34, 0x97, 0x5c, 0x58, 0x9c, - 0xec, 0xa3, 0xad, 0x3d, 0x26, 0x64, 0xf1, 0xac, 0xd2, 0x43, 0x59, 0x72, 0x3d, 0x39, 0x64, 0x1a, - 0xfa, 0xa0, 0x01, 0xef, 0xa2, 0x55, 0xe8, 0x17, 0x5c, 0x34, 0xfc, 0x96, 0xff, 0xe4, 0xff, 0xf8, - 0xce, 0x62, 0x1a, 0xd6, 0x27, 0x50, 0x1c, 0x75, 0x89, 0x49, 0x93, 0xc4, 0xc2, 0xdd, 0xda, 0xcf, - 0x93, 0xd0, 0x27, 0x25, 0xaa, 0x1f, 0xca, 0x7e, 0x75, 0xc4, 0x5e, 0x41, 0x09, 0x85, 0xc2, 0x19, - 0x6a, 0xf6, 0x97, 0xba, 0x69, 0x5e, 0x32, 0xd0, 0x5c, 0x8a, 0x74, 0x00, 0x2a, 0xcd, 0xa5, 0x50, - 0x55, 0xc1, 0x8c, 0x64, 0x2d, 0x7e, 0xbc, 0x98, 0x86, 0x3b, 0x56, 0xf2, 0x66, 0x2e, 0x49, 0x1e, - 0x18, 0xb0, 0xe7, 0xb0, 0x03, 0x50, 0x3d, 0x87, 0x3c, 0x47, 0xeb, 0x6f, 0xf6, 0x8d, 0x7b, 0xdc, - 0x40, 0xeb, 0xa6, 0x58, 0x96, 0xd6, 0x6e, 0x72, 0x1e, 0xe2, 0x26, 0xda, 0x50, 0x55, 0x66, 0x24, - 0x1a, 0x2b, 0x06, 0xfa, 0x1d, 0x77, 0x37, 0x3e, 0x9f, 0x84, 0xde, 0xf1, 0xf7, 0x96, 0x47, 0xbe, - 0xf8, 0xa8, 0x7e, 0xc0, 0x04, 0x53, 0x5c, 0xbd, 0x5e, 0xae, 0x01, 0xc7, 0x68, 0x6d, 0x64, 0x3a, - 0x31, 0x7a, 0x9b, 0x9d, 0x47, 0xd1, 0xf5, 0x1b, 0x89, 0x2e, 0x76, 0x1d, 0xd7, 0x4e, 0xa7, 0xa1, - 0x97, 0xb8, 0x4a, 0xfc, 0x1e, 0xdd, 0x76, 0xc4, 0xd4, 0xfc, 0x4f, 0x35, 0x56, 0x5a, 0xff, 0xfd, - 0x4b, 0xcb, 0x39, 0x30, 0x2d, 0xc5, 0x0f, 0x97, 0x5a, 0x8b, 0x69, 0x78, 0xdf, 0x4e, 0xe8, 0xb2, - 0x12, 0x49, 0x6e, 0xb9, 0xc4, 0x9e, 0x8d, 0xbf, 0xfe, 0x69, 0xc0, 0x4e, 0x64, 0x17, 0xad, 0xda, - 0xa6, 0xaf, 0xac, 0xcf, 0xa4, 0x49, 0x62, 0x61, 0x7c, 0xec, 0x23, 0x0c, 0xe7, 0xcb, 0x4f, 0x0b, - 0xb7, 0x7d, 0x33, 0xaa, 0xcd, 0x4e, 0x74, 0x93, 0xd3, 0xeb, 0x6f, 0x26, 0xde, 0x71, 0x9e, 0xb7, - 0xdd, 0xa1, 0x5c, 0xd1, 0x25, 0xc9, 0x5d, 0xf8, 0xbb, 0xca, 0x5e, 0x50, 0xfc, 0xe2, 0x74, 0x16, - 0xf8, 0x67, 0xb3, 0xc0, 0xff, 0x31, 0x0b, 0xfc, 0x4f, 0xf3, 0xc0, 0x3b, 0x9b, 0x07, 0xde, 0xb7, - 0x79, 0xe0, 0xbd, 0xed, 0x0c, 0xb8, 0x1e, 0x56, 0x59, 0x94, 0xcb, 0x82, 0xbe, 0x34, 0x7e, 0x7a, - 0x43, 0xe0, 0x82, 0xba, 0x77, 0xf4, 0xf1, 0xf2, 0x4b, 0xd2, 0x93, 0x11, 0x53, 0xd9, 0x9a, 0x39, - 0xef, 0xa7, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xd7, 0xfc, 0x2e, 0x39, 0x6d, 0x03, 0x00, 0x00, + // 492 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xcb, 0x6e, 0xd3, 0x40, + 0x14, 0xb5, 0x4b, 0xda, 0xc2, 0x34, 0x20, 0x18, 0x41, 0x49, 0x23, 0x61, 0xa7, 0x23, 0xa8, 0x58, + 0x79, 0x94, 0x20, 0x36, 0xd9, 0xe1, 0x94, 0x76, 0x55, 0x40, 0x86, 0x15, 0x1b, 0xeb, 0xda, 0x19, + 0x92, 0x81, 0x7a, 0x26, 0xf2, 0x8c, 0x23, 0xb2, 0xeb, 0x27, 0xf0, 0x09, 0x7c, 0x09, 0x6c, 0xbb, + 0xec, 0x92, 0x05, 0x8a, 0x50, 0xb2, 0x61, 0x9d, 0x2f, 0x40, 0x99, 0x99, 0x42, 0x4b, 0x1f, 0x3b, + 0xdf, 0x7b, 0xce, 0x3d, 0x3e, 0xf7, 0x31, 0x88, 0x08, 0x9e, 0xf1, 0xb2, 0xa2, 0x5a, 0x7e, 0x62, + 0xe2, 0x03, 0xe4, 0x5a, 0x96, 0x13, 0x3a, 0x6e, 0x53, 0xa5, 0x41, 0xb3, 0x68, 0x54, 0x4a, 0x2d, + 0xf1, 0xa6, 0xe5, 0x44, 0x67, 0x39, 0xd1, 0xb8, 0xdd, 0xbc, 0x3f, 0x90, 0x03, 0x69, 0x28, 0x74, + 0xf9, 0x65, 0xd9, 0xcd, 0xad, 0x5c, 0xaa, 0x42, 0xaa, 0xd4, 0x02, 0x36, 0x70, 0x50, 0x60, 0x23, + 0x9a, 0x81, 0x62, 0x74, 0xdc, 0xce, 0x98, 0x86, 0x36, 0xcd, 0x25, 0x17, 0x16, 0x27, 0x7b, 0x68, + 0x73, 0x97, 0x09, 0x59, 0xbc, 0xa8, 0xf4, 0x50, 0x96, 0x5c, 0x4f, 0x0e, 0x98, 0x86, 0x3e, 0x68, + 0xc0, 0x3b, 0x68, 0x15, 0xfa, 0x05, 0x17, 0x0d, 0xbf, 0xe5, 0x3f, 0xbd, 0x15, 0xdf, 0x5d, 0x4c, + 0xc3, 0xfa, 0x04, 0x8a, 0xc3, 0x2e, 0x31, 0x69, 0x92, 0x58, 0xb8, 0x5b, 0xfb, 0xfd, 0x35, 0xf4, + 0x49, 0x89, 0xea, 0x07, 0xb2, 0x5f, 0x1d, 0xb2, 0x37, 0x50, 0x42, 0xa1, 0x70, 0x86, 0x9a, 0xfd, + 0xa5, 0x6e, 0x9a, 0x97, 0x0c, 0x34, 0x97, 0x22, 0x1d, 0x80, 0x4a, 0x73, 0x29, 0x54, 0x55, 0x30, + 0x23, 0x59, 0x8b, 0x9f, 0x2c, 0xa6, 0xe1, 0xb6, 0x95, 0xbc, 0x9a, 0x4b, 0x92, 0x87, 0x06, 0xec, + 0x39, 0x6c, 0x1f, 0x54, 0xcf, 0x21, 0x2f, 0xd1, 0xfa, 0xbb, 0x3d, 0xe3, 0x1e, 0x37, 0xd0, 0xba, + 0x29, 0x96, 0xa5, 0xb5, 0x9b, 0x9c, 0x86, 0xb8, 0x89, 0x6e, 0xaa, 0x2a, 0x33, 0x12, 0x8d, 0x15, + 0x03, 0xfd, 0x8d, 0xbb, 0xb5, 0xa3, 0x9f, 0x2d, 0x8f, 0x7c, 0xf3, 0x51, 0x7d, 0x9f, 0x09, 0xa6, + 0xb8, 0x7a, 0xbb, 0x5c, 0x01, 0x8e, 0xd1, 0xda, 0xc8, 0x74, 0x61, 0xb4, 0x36, 0x3a, 0x8f, 0xa3, + 0xcb, 0xb7, 0x11, 0x9d, 0xed, 0x38, 0xae, 0x1d, 0x4f, 0x43, 0x2f, 0x71, 0x95, 0xf8, 0x23, 0xba, + 0xe3, 0x88, 0xa9, 0xf9, 0x97, 0x6a, 0xac, 0xb4, 0x6e, 0x5c, 0xa7, 0xe5, 0x1c, 0x98, 0x76, 0xe2, + 0x47, 0x4b, 0xad, 0xc5, 0x34, 0x7c, 0x60, 0xa7, 0x73, 0x5e, 0x89, 0x24, 0xb7, 0x5d, 0x62, 0xd7, + 0xc6, 0xdf, 0xff, 0x35, 0x60, 0xa7, 0xb1, 0x83, 0x56, 0x6d, 0xc3, 0x17, 0x56, 0x67, 0xd2, 0x24, + 0xb1, 0x30, 0x3e, 0xf2, 0x11, 0x86, 0xd3, 0xc5, 0xa7, 0x85, 0xdb, 0xbc, 0x19, 0xd3, 0x46, 0x27, + 0xba, 0xca, 0xe9, 0xe5, 0xf7, 0x12, 0x6f, 0x3b, 0xcf, 0x5b, 0xee, 0x48, 0x2e, 0xe8, 0x92, 0xe4, + 0x1e, 0xfc, 0x5f, 0x65, 0xaf, 0x27, 0x7e, 0x7d, 0x3c, 0x0b, 0xfc, 0x93, 0x59, 0xe0, 0xff, 0x9a, + 0x05, 0xfe, 0x97, 0x79, 0xe0, 0x9d, 0xcc, 0x03, 0xef, 0xc7, 0x3c, 0xf0, 0xde, 0x3f, 0x1f, 0x70, + 0x3d, 0xac, 0xb2, 0x28, 0x97, 0x05, 0x7d, 0x65, 0xfc, 0xf4, 0x86, 0xc0, 0x05, 0x75, 0x6f, 0x68, + 0xdc, 0xa1, 0x9f, 0xcf, 0x3f, 0x24, 0x3d, 0x19, 0x31, 0x95, 0xad, 0x99, 0xeb, 0x7e, 0xf6, 0x27, + 0x00, 0x00, 0xff, 0xff, 0xb2, 0x87, 0x6f, 0x1a, 0x6c, 0x03, 0x00, 0x00, } func (this *DenomAuthorityMetadata) Equal(that interface{}) bool { diff --git a/x/tokenfactory/types/state_test.go b/x/tokenfactory/types/state_test.go index d532d47d5..51ad0f64a 100644 --- a/x/tokenfactory/types/state_test.go +++ b/x/tokenfactory/types/state_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) func TestDenomStr_Validate(t *testing.T) { @@ -35,7 +35,7 @@ func TestDenomStr_Validate(t *testing.T) { } require.NoError(t, err) assert.Equal(t, tfDenom.Denom(), tc.denom) - assert.Equal(t, tfDenom.String(), string(tc.denom)) + assert.Equal(t, tfDenom.Denom().String(), string(tc.denom)) assert.NoError(t, tfDenom.Validate()) assert.NotPanics(t, func() { @@ -68,7 +68,7 @@ func TestGenesisState(t *testing.T) { Denom: types.TFDenom{ Creator: creator.String(), Subdenom: testutil.Latin.Letters[lettersIdx : lettersIdx+4], - }.String(), + }.Denom().String(), AuthorityMetadata: types.DenomAuthorityMetadata{ Admin: creator.String(), }, diff --git a/x/tokenfactory/types/tx.pb.go b/x/tokenfactory/types/tx.pb.go index 25a92dacf..3bc112f12 100644 --- a/x/tokenfactory/types/tx.pb.go +++ b/x/tokenfactory/types/tx.pb.go @@ -740,59 +740,59 @@ func init() { func init() { proto.RegisterFile("nibiru/tokenfactory/v1/tx.proto", fileDescriptor_4c78bacd179e004d) } var fileDescriptor_4c78bacd179e004d = []byte{ - // 826 bytes of a gzipped FileDescriptorProto + // 829 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0xe3, 0x44, 0x14, 0x8f, 0xd9, 0x6e, 0x36, 0x99, 0xd2, 0x3f, 0xeb, 0x96, 0x34, 0x6b, 0xd4, 0x18, 0x8d, 0x60, - 0x81, 0x45, 0xb5, 0x95, 0xae, 0xe0, 0xb0, 0x17, 0xb4, 0x2e, 0xe2, 0x84, 0x57, 0x2b, 0x6f, 0xb9, - 0x20, 0xa4, 0x68, 0x12, 0x4f, 0x1d, 0xab, 0xf5, 0x4c, 0xe4, 0x99, 0xa4, 0x0d, 0x07, 0x24, 0xf8, - 0x04, 0x9c, 0xf8, 0x02, 0x9c, 0x10, 0x17, 0x0e, 0x7c, 0x88, 0xde, 0xa8, 0x38, 0x71, 0xb2, 0x50, - 0x7b, 0xe0, 0x9e, 0x4f, 0xb0, 0x9a, 0x3f, 0xb5, 0xdd, 0x26, 0x4d, 0x9b, 0x4b, 0x6f, 0x63, 0xbf, - 0xdf, 0xfb, 0xbd, 0xdf, 0xcf, 0x7e, 0xef, 0x69, 0x80, 0x4d, 0xe2, 0x6e, 0x9c, 0x0e, 0x5d, 0x4e, - 0x0f, 0x31, 0x39, 0x40, 0x3d, 0x4e, 0xd3, 0xb1, 0x3b, 0x6a, 0xbb, 0xfc, 0xc4, 0x19, 0xa4, 0x94, - 0x53, 0xb3, 0xa1, 0x00, 0x4e, 0x19, 0xe0, 0x8c, 0xda, 0xd6, 0x66, 0x44, 0x23, 0x2a, 0x21, 0xae, - 0x38, 0x29, 0xb4, 0xd5, 0xea, 0x51, 0x96, 0x50, 0xe6, 0x76, 0x11, 0xc3, 0xee, 0xa8, 0xdd, 0xc5, - 0x1c, 0xb5, 0xdd, 0x1e, 0x8d, 0x89, 0x8e, 0x6f, 0xe9, 0x78, 0xc2, 0x22, 0x51, 0x25, 0x61, 0x91, - 0x0e, 0x3c, 0x51, 0x81, 0x8e, 0x62, 0x54, 0x0f, 0x53, 0x9c, 0xe4, 0x30, 0xe7, 0x14, 0x0f, 0x3a, - 0x0e, 0x6f, 0xb0, 0xc0, 0x38, 0xe2, 0x58, 0x61, 0xe0, 0x11, 0x58, 0xf5, 0x59, 0xb4, 0x97, 0x62, - 0xc4, 0xf1, 0x57, 0x98, 0xd0, 0xc4, 0xfc, 0x14, 0x54, 0x19, 0x26, 0x21, 0x4e, 0x9b, 0xc6, 0x07, - 0xc6, 0x27, 0x75, 0xef, 0xf1, 0x24, 0xb3, 0x57, 0xc6, 0x28, 0x39, 0x7a, 0x01, 0xd5, 0x7b, 0x18, - 0x68, 0x80, 0xe9, 0x82, 0x1a, 0x1b, 0x76, 0x43, 0x91, 0xd6, 0x7c, 0x47, 0x82, 0x37, 0x26, 0x99, - 0xbd, 0xa6, 0xc1, 0x3a, 0x02, 0x83, 0x1c, 0x04, 0xbf, 0x07, 0x8d, 0xab, 0xd5, 0x02, 0xcc, 0x06, - 0x94, 0x30, 0x6c, 0x7a, 0x60, 0x8d, 0xe0, 0xe3, 0x8e, 0x94, 0xda, 0x51, 0x8c, 0xaa, 0xbc, 0x35, - 0xc9, 0xec, 0x86, 0x62, 0xbc, 0x06, 0x80, 0xc1, 0x0a, 0xc1, 0xc7, 0xfb, 0xe2, 0x85, 0xe4, 0x82, - 0xbf, 0x1a, 0xca, 0x4c, 0x1f, 0x91, 0x08, 0xbf, 0x0c, 0x93, 0x98, 0x2c, 0x62, 0xe6, 0x29, 0x78, - 0x58, 0x76, 0xb2, 0x3e, 0xc9, 0xec, 0x77, 0x15, 0x52, 0x57, 0x53, 0x61, 0xb3, 0x0d, 0xea, 0x42, - 0x08, 0x12, 0xfc, 0xcd, 0x07, 0x12, 0xbb, 0x39, 0xc9, 0xec, 0xf5, 0x42, 0xa3, 0x0c, 0xc1, 0xa0, - 0x46, 0xf0, 0xb1, 0x54, 0x01, 0x9b, 0xca, 0x76, 0xa1, 0xeb, 0xd2, 0x36, 0xfc, 0xcd, 0x00, 0xef, - 0xf9, 0x2c, 0xfa, 0x76, 0x10, 0x22, 0x8e, 0x7d, 0x1a, 0x0e, 0x8f, 0xf0, 0x6b, 0x94, 0xa2, 0x84, - 0x99, 0x5f, 0x80, 0x3a, 0x1a, 0xf2, 0x3e, 0x4d, 0x63, 0x3e, 0xd6, 0xe2, 0x9b, 0xff, 0xfc, 0xb5, - 0xb3, 0xa9, 0x3b, 0xe0, 0x65, 0x18, 0xa6, 0x98, 0xb1, 0x37, 0x3c, 0x8d, 0x49, 0x14, 0x14, 0x50, - 0xd3, 0x03, 0xd5, 0x81, 0x64, 0x90, 0x3e, 0x96, 0x77, 0x3f, 0x74, 0x66, 0xf7, 0xa9, 0x53, 0xae, - 0xe6, 0x2d, 0x9d, 0x66, 0x76, 0x25, 0xd0, 0x99, 0x2f, 0x56, 0x7f, 0xfe, 0xff, 0xcf, 0x67, 0x05, - 0x27, 0xb4, 0xc1, 0xf6, 0x4c, 0x91, 0xb9, 0x8d, 0xdf, 0x0d, 0xf0, 0xc8, 0x67, 0x91, 0x1f, 0x13, - 0xbe, 0xc8, 0x27, 0xf7, 0xc0, 0x92, 0x18, 0x01, 0xad, 0xf4, 0x89, 0xa3, 0xbd, 0x89, 0x19, 0x71, - 0x74, 0x3f, 0x3b, 0x7b, 0x34, 0x26, 0xde, 0x86, 0x90, 0x37, 0xc9, 0xec, 0x65, 0xc5, 0x23, 0x92, - 0x60, 0x20, 0x73, 0x4d, 0x17, 0x3c, 0x4a, 0x62, 0xc2, 0x3b, 0x9c, 0xea, 0x9f, 0xd1, 0x38, 0xcd, - 0x6c, 0x63, 0x92, 0xd9, 0xab, 0x0a, 0xab, 0x83, 0x30, 0xa8, 0x8a, 0xd3, 0x3e, 0x85, 0xcf, 0xc0, - 0x9a, 0x96, 0x9a, 0x37, 0xdf, 0x56, 0xc1, 0x21, 0x35, 0xe7, 0xd8, 0x3f, 0x94, 0x2f, 0x6f, 0x98, - 0x92, 0xfb, 0xf6, 0xd5, 0x06, 0xf5, 0xee, 0x30, 0x25, 0x9d, 0x83, 0x94, 0x26, 0xd3, 0x6d, 0x96, - 0x87, 0x60, 0x50, 0x13, 0xe7, 0xaf, 0xc5, 0xf1, 0xb1, 0x74, 0x26, 0xc4, 0xe6, 0x3f, 0xe6, 0x27, - 0x03, 0x6c, 0xf8, 0x2c, 0x7a, 0x83, 0xb9, 0x1c, 0x11, 0x1f, 0x73, 0x14, 0x22, 0x8e, 0x16, 0x31, - 0xf3, 0x25, 0xa8, 0x25, 0x3a, 0x4d, 0x1b, 0xda, 0x2e, 0x0c, 0x91, 0xc3, 0xdc, 0xd0, 0x25, 0xb7, - 0xee, 0xa5, 0x3c, 0x09, 0x6e, 0x83, 0xf7, 0x67, 0x48, 0xc8, 0x25, 0xfe, 0x08, 0x56, 0xb4, 0xea, - 0x57, 0x88, 0xc7, 0x23, 0x7c, 0xcf, 0x1f, 0x1a, 0x6e, 0xc9, 0x09, 0x2c, 0xea, 0x5f, 0x0a, 0xdb, - 0xfd, 0xfb, 0x21, 0x78, 0xe0, 0xb3, 0xc8, 0xc4, 0x60, 0xb9, 0xbc, 0x1f, 0x9f, 0xde, 0x38, 0x50, - 0x57, 0x36, 0x9b, 0xe5, 0xdc, 0x0d, 0x97, 0x37, 0xa1, 0x28, 0x53, 0xda, 0x5c, 0x73, 0xcb, 0x14, - 0xb8, 0xf9, 0x65, 0xa6, 0x37, 0x8e, 0xf9, 0x03, 0x30, 0x67, 0x6c, 0x9b, 0x9d, 0x39, 0x2c, 0xd3, - 0x70, 0xeb, 0xf3, 0x85, 0xe0, 0x79, 0xed, 0xd7, 0x60, 0x49, 0xae, 0x08, 0x7b, 0x4e, 0xba, 0x00, - 0x58, 0x1f, 0xdf, 0x02, 0x28, 0x33, 0xca, 0xe1, 0x9c, 0xc7, 0x28, 0x00, 0x73, 0x19, 0xcb, 0x13, - 0x63, 0x72, 0xb0, 0x3e, 0x35, 0x2d, 0x9f, 0xcd, 0x49, 0xbe, 0x0e, 0xb6, 0x9e, 0x2f, 0x00, 0xce, - 0xab, 0x86, 0x00, 0x94, 0x26, 0xe0, 0xa3, 0x5b, 0xc4, 0x2a, 0x98, 0xb5, 0x73, 0x27, 0x58, 0x3e, - 0x68, 0x15, 0xef, 0x9b, 0xd3, 0xf3, 0x96, 0x71, 0x76, 0xde, 0x32, 0xfe, 0x3b, 0x6f, 0x19, 0xbf, - 0x5c, 0xb4, 0x2a, 0x67, 0x17, 0xad, 0xca, 0xbf, 0x17, 0xad, 0xca, 0x77, 0xbb, 0x51, 0xcc, 0xfb, - 0xc3, 0xae, 0xd3, 0xa3, 0x89, 0xfb, 0x4a, 0x92, 0xee, 0xf5, 0x51, 0x4c, 0x5c, 0x7d, 0x83, 0x38, - 0xb9, 0x7a, 0x87, 0xe0, 0xe3, 0x01, 0x66, 0xdd, 0xaa, 0xbc, 0x41, 0x3c, 0x7f, 0x1b, 0x00, 0x00, - 0xff, 0xff, 0xb6, 0x28, 0x76, 0x66, 0x2a, 0x09, 0x00, 0x00, + 0x81, 0x45, 0xb5, 0x95, 0xae, 0x96, 0xc3, 0x5e, 0xd0, 0xba, 0x88, 0x9b, 0x97, 0x95, 0xb7, 0x5c, + 0x10, 0x52, 0x34, 0x89, 0xa7, 0x8e, 0xd5, 0x7a, 0x26, 0xf2, 0x4c, 0xd2, 0x86, 0x03, 0x12, 0x7c, + 0x02, 0x4e, 0x7c, 0x01, 0x4e, 0x88, 0x0b, 0x07, 0x3e, 0x44, 0x6f, 0x54, 0x9c, 0x38, 0x59, 0xa8, + 0x3d, 0x70, 0xcf, 0x27, 0x40, 0xf3, 0xa7, 0xb6, 0xdb, 0xa4, 0x69, 0x73, 0xe9, 0x6d, 0xec, 0xf7, + 0x7b, 0xbf, 0xf7, 0xfb, 0xd9, 0xef, 0x3d, 0x0d, 0xb0, 0x49, 0xdc, 0x8d, 0xd3, 0xa1, 0xcb, 0xe9, + 0x21, 0x26, 0x07, 0xa8, 0xc7, 0x69, 0x3a, 0x76, 0x47, 0x6d, 0x97, 0x9f, 0x38, 0x83, 0x94, 0x72, + 0x6a, 0x36, 0x14, 0xc0, 0x29, 0x03, 0x9c, 0x51, 0xdb, 0xda, 0x8c, 0x68, 0x44, 0x25, 0xc4, 0x15, + 0x27, 0x85, 0xb6, 0x5a, 0x3d, 0xca, 0x12, 0xca, 0xdc, 0x2e, 0x62, 0xd8, 0x1d, 0xb5, 0xbb, 0x98, + 0xa3, 0xb6, 0xdb, 0xa3, 0x31, 0xd1, 0xf1, 0x2d, 0x1d, 0x4f, 0x58, 0x24, 0xaa, 0x24, 0x2c, 0xd2, + 0x81, 0x27, 0x2a, 0xd0, 0x51, 0x8c, 0xea, 0x61, 0x8a, 0x93, 0x1c, 0xe6, 0x9c, 0xe2, 0x41, 0xc7, + 0xe1, 0x0d, 0x16, 0x18, 0x47, 0x1c, 0x2b, 0x0c, 0x3c, 0x02, 0xab, 0x3e, 0x8b, 0xf6, 0x52, 0x8c, + 0x38, 0xfe, 0x12, 0x13, 0x9a, 0x98, 0x9f, 0x82, 0x2a, 0xc3, 0x24, 0xc4, 0x69, 0xd3, 0xf8, 0xc0, + 0xf8, 0xa4, 0xee, 0x3d, 0x9e, 0x64, 0xf6, 0xca, 0x18, 0x25, 0x47, 0x2f, 0xa1, 0x7a, 0x0f, 0x03, + 0x0d, 0x30, 0x5d, 0x50, 0x63, 0xc3, 0x6e, 0x28, 0xd2, 0x9a, 0xef, 0x48, 0xf0, 0xc6, 0x24, 0xb3, + 0xd7, 0x34, 0x58, 0x47, 0x60, 0x90, 0x83, 0xe0, 0x77, 0xa0, 0x71, 0xb5, 0x5a, 0x80, 0xd9, 0x80, + 0x12, 0x86, 0x4d, 0x0f, 0xac, 0x11, 0x7c, 0xdc, 0x91, 0x52, 0x3b, 0x8a, 0x51, 0x95, 0xb7, 0x26, + 0x99, 0xdd, 0x50, 0x8c, 0xd7, 0x00, 0x30, 0x58, 0x21, 0xf8, 0x78, 0x5f, 0xbc, 0x90, 0x5c, 0xf0, + 0x17, 0x43, 0x99, 0xe9, 0x23, 0x12, 0xe1, 0x57, 0x61, 0x12, 0x93, 0x45, 0xcc, 0x3c, 0x05, 0x0f, + 0xcb, 0x4e, 0xd6, 0x27, 0x99, 0xfd, 0xae, 0x42, 0xea, 0x6a, 0x2a, 0x6c, 0xb6, 0x41, 0x5d, 0x08, + 0x41, 0x82, 0xbf, 0xf9, 0x40, 0x62, 0x37, 0x27, 0x99, 0xbd, 0x5e, 0x68, 0x94, 0x21, 0x18, 0xd4, + 0x08, 0x3e, 0x96, 0x2a, 0x60, 0x53, 0xd9, 0x2e, 0x74, 0x5d, 0xda, 0x86, 0xbf, 0x1a, 0xe0, 0x3d, + 0x9f, 0x45, 0xdf, 0x0c, 0x42, 0xc4, 0xb1, 0x4f, 0xc3, 0xe1, 0x11, 0x7e, 0x83, 0x52, 0x94, 0x30, + 0xf3, 0x73, 0x50, 0x47, 0x43, 0xde, 0xa7, 0x69, 0xcc, 0xc7, 0x5a, 0x7c, 0xf3, 0xef, 0x3f, 0x77, + 0x36, 0x75, 0x07, 0xbc, 0x0a, 0xc3, 0x14, 0x33, 0xf6, 0x96, 0xa7, 0x31, 0x89, 0x82, 0x02, 0x6a, + 0x7a, 0xa0, 0x3a, 0x90, 0x0c, 0xd2, 0xc7, 0xf2, 0xee, 0x87, 0xce, 0xec, 0x3e, 0x75, 0xca, 0xd5, + 0xbc, 0xa5, 0xd3, 0xcc, 0xae, 0x04, 0x3a, 0xf3, 0xe5, 0xea, 0x4f, 0xff, 0xfd, 0xf1, 0xac, 0xe0, + 0x84, 0x36, 0xd8, 0x9e, 0x29, 0x32, 0xb7, 0xf1, 0x9b, 0x01, 0x1e, 0xf9, 0x2c, 0xf2, 0x63, 0xc2, + 0x17, 0xf9, 0xe4, 0x1e, 0x58, 0x12, 0x23, 0xa0, 0x95, 0x3e, 0x71, 0xb4, 0x37, 0x31, 0x23, 0x8e, + 0xee, 0x67, 0x67, 0x8f, 0xc6, 0xc4, 0xdb, 0x10, 0xf2, 0x26, 0x99, 0xbd, 0xac, 0x78, 0x44, 0x12, + 0x0c, 0x64, 0xae, 0xe9, 0x82, 0x47, 0x49, 0x4c, 0x78, 0x87, 0x53, 0xfd, 0x33, 0x1a, 0xa7, 0x99, + 0x6d, 0x4c, 0x32, 0x7b, 0x55, 0x61, 0x75, 0x10, 0x06, 0x55, 0x71, 0xda, 0xa7, 0xf0, 0x19, 0x58, + 0xd3, 0x52, 0xf3, 0xe6, 0xdb, 0x2a, 0x38, 0xa4, 0xe6, 0x1c, 0xfb, 0xbb, 0xf2, 0xe5, 0x0d, 0x53, + 0x72, 0xdf, 0xbe, 0xda, 0xa0, 0xde, 0x1d, 0xa6, 0xa4, 0x73, 0x90, 0xd2, 0x64, 0xba, 0xcd, 0xf2, + 0x10, 0x0c, 0x6a, 0xe2, 0xfc, 0x95, 0x38, 0x3e, 0x96, 0xce, 0x84, 0xd8, 0xfc, 0xc7, 0xfc, 0x68, + 0x80, 0x0d, 0x9f, 0x45, 0x6f, 0x31, 0x97, 0x23, 0xe2, 0x63, 0x8e, 0x42, 0xc4, 0xd1, 0x22, 0x66, + 0xbe, 0x00, 0xb5, 0x44, 0xa7, 0x69, 0x43, 0xdb, 0x85, 0x21, 0x72, 0x98, 0x1b, 0xba, 0xe4, 0xd6, + 0xbd, 0x94, 0x27, 0xc1, 0x6d, 0xf0, 0xfe, 0x0c, 0x09, 0xb9, 0xc4, 0x1f, 0xc0, 0x8a, 0x56, 0xfd, + 0x1a, 0xf1, 0x78, 0x84, 0xef, 0xf9, 0x43, 0xc3, 0x2d, 0x39, 0x81, 0x45, 0xfd, 0x4b, 0x61, 0xbb, + 0x7f, 0x3d, 0x04, 0x0f, 0x7c, 0x16, 0x99, 0x18, 0x2c, 0x97, 0xf7, 0xe3, 0xd3, 0x1b, 0x07, 0xea, + 0xca, 0x66, 0xb3, 0x9c, 0xbb, 0xe1, 0xf2, 0x26, 0x14, 0x65, 0x4a, 0x9b, 0x6b, 0x6e, 0x99, 0x02, + 0x37, 0xbf, 0xcc, 0xf4, 0xc6, 0x31, 0xbf, 0x07, 0xe6, 0x8c, 0x6d, 0xb3, 0x33, 0x87, 0x65, 0x1a, + 0x6e, 0xbd, 0x58, 0x08, 0x9e, 0xd7, 0x7e, 0x03, 0x96, 0xe4, 0x8a, 0xb0, 0xe7, 0xa4, 0x0b, 0x80, + 0xf5, 0xf1, 0x2d, 0x80, 0x32, 0xa3, 0x1c, 0xce, 0x79, 0x8c, 0x02, 0x30, 0x97, 0xb1, 0x3c, 0x31, + 0x26, 0x07, 0xeb, 0x53, 0xd3, 0xf2, 0xd9, 0x9c, 0xe4, 0xeb, 0x60, 0xeb, 0xf9, 0x02, 0xe0, 0xbc, + 0x6a, 0x08, 0x40, 0x69, 0x02, 0x3e, 0xba, 0x45, 0xac, 0x82, 0x59, 0x3b, 0x77, 0x82, 0xe5, 0x83, + 0x56, 0xf1, 0xbe, 0x3e, 0x3d, 0x6f, 0x19, 0x67, 0xe7, 0x2d, 0xe3, 0xdf, 0xf3, 0x96, 0xf1, 0xf3, + 0x45, 0xab, 0x72, 0x76, 0xd1, 0xaa, 0xfc, 0x73, 0xd1, 0xaa, 0x7c, 0xfb, 0x22, 0x8a, 0x79, 0x7f, + 0xd8, 0x75, 0x7a, 0x34, 0x71, 0x5f, 0x4b, 0xd2, 0xbd, 0x3e, 0x8a, 0x89, 0xab, 0x6f, 0x10, 0xa3, + 0x5d, 0xf7, 0xe4, 0xea, 0x35, 0x82, 0x8f, 0x07, 0x98, 0x75, 0xab, 0xf2, 0x12, 0xf1, 0xfc, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x14, 0x71, 0x2c, 0x23, 0x2d, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/x/tokenfactory/types/tx_msgs_test.go b/x/tokenfactory/types/tx_msgs_test.go index ae28bf3d8..c71033c7f 100644 --- a/x/tokenfactory/types/tx_msgs_test.go +++ b/x/tokenfactory/types/tx_msgs_test.go @@ -10,10 +10,11 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + "cosmossdk.io/math" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/NibiruChain/nibiru/x/common/testutil" - "github.com/NibiruChain/nibiru/x/tokenfactory/types" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/tokenfactory/types" ) type ValidateBasicTest struct { @@ -214,7 +215,7 @@ func TestMsgMint_ValidateBasic(t *testing.T) { name: "invalid denom", msg: &types.MsgMint{ Sender: sbf, - Coin: sdk.Coin{Denom: "tf/", Amount: sdk.NewInt(420)}, + Coin: sdk.Coin{Denom: "tf/", Amount: math.NewInt(420)}, MintTo: "", }, wantErr: "denom format error", @@ -232,7 +233,7 @@ func TestMsgMint_ValidateBasic(t *testing.T) { name: "invalid coin", msg: &types.MsgMint{ Sender: sbf, - Coin: sdk.Coin{Amount: sdk.NewInt(-420)}, + Coin: sdk.Coin{Amount: math.NewInt(-420)}, MintTo: "", }, wantErr: "invalid coin", @@ -269,7 +270,7 @@ func TestMsgBurn_ValidateBasic(t *testing.T) { name: "invalid denom", msg: &types.MsgBurn{ Sender: sbf, - Coin: sdk.Coin{Denom: "tf/", Amount: sdk.NewInt(420)}, + Coin: sdk.Coin{Denom: "tf/", Amount: math.NewInt(420)}, BurnFrom: "", }, wantErr: "denom format error", @@ -287,7 +288,7 @@ func TestMsgBurn_ValidateBasic(t *testing.T) { name: "invalid coin", msg: &types.MsgBurn{ Sender: sbf, - Coin: sdk.Coin{Amount: sdk.NewInt(-420)}, + Coin: sdk.Coin{Amount: math.NewInt(-420)}, BurnFrom: "", }, wantErr: "invalid coin",